Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Micro Frontends の理論と実践 -価値提供を高速化する真のマイクロサービスのあ...
Search
nobuhikosawai
November 02, 2018
Technology
41k
17
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Micro Frontends の理論と実践 -価値提供を高速化する真のマイクロサービスのあり方- / The Theory and Practice of Micro Frontends
AWS Dev Day Tokyo 2018でMicro Frontendsについて話したスライドです。
#MicroFrontends #マイクロフロントエンド
nobuhikosawai
November 02, 2018
More Decks by nobuhikosawai
See All by nobuhikosawai
GraphQLを用いたサイトに おけるパフォーマンス改善 (ECサイトを題材に)/ Improving online shopping site performance which using the GraphQL
nobuhikosawai
3
6.2k
Micro Frontends (example from micro-frontends.org)
nobuhikosawai
0
1k
Railsのタイムゾーン
nobuhikosawai
4
2.6k
Other Decks in Technology
See All in Technology
Snowflakeで実現する全社横断の顧客の声(VOC)分析・活用基盤@Snowflake World Tour Tokyo 2026
yuto16
0
120
Podは生きているのにGoだけが落ちる:GOGCとGOMEMLIMITで追うInvisible OOM Killの謎
tkc66buzz
1
210
2026-09-11 【Snowflake World Tour Tokyo 2026】Snowflakeを起点に、AI Agentが自律稼働し続ける未来へ / Driving AI Agents with Snowflake
civitaspo
0
220
Screen Lens - 今見てる画面を翻訳する
komagata
0
140
Redmine 7.0で私が開発した新機能の狙いと背景
vividtone
1
130
コスト最適化の「めんどくさい」を AWS FinOps Agent でチョット楽にする
classmethod_kaz
0
280
Bet AI Day 2026丨AIを「使う」から、AIが「働く」へ ― LayerXが進める「組織AI」の社会実装
layerx
PRO
3
2.8k
『止めない』を設計する — 制約の中で、事業の根幹を支える判断
hiroyaterui
0
230
DEFCON34-Write-up_HYCu-MYCu
daikiokazaki
0
110
V8コントリビュート超入門
riyaamemiya
0
150
module Synths; end
asonas
1
130
Oracle Cloud Infrastructure IaaS 新機能アップデート 2026/6 - 2026/8
oracle4engineer
PRO
0
130
Featured
See All Featured
Optimizing for Happiness
mojombo
378
71k
Embracing the Ebb and Flow
colly
88
5.2k
The Language of Interfaces
destraynor
162
27k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
600
Marketing to machines
jonoalderson
1
5.8k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
4
600
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
520
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
380
Amusing Abliteration
ianozsvald
1
290
How to train your dragon (web standard)
notwaldorf
97
6.8k
We Are The Robots
honzajavorek
0
350
Bash Introduction
62gerente
615
220k
Transcript
.JDSP'SPOUFOETͷཧͱ࣮ફ ՁఏڙΛߴԽ͢ΔਅͷϚΠΫϩαʔϏεͷ͋Γํ ᖒҪએ !OPCVIJLPTBXBJ גࣜձࣾ'J/$5FDIOPMPHJFT "84%FW%BZ5PLZP
ࠓશ͘͜Ε·Ͱͱҧ͏ ϚΠΫϩαʔϏεͷΛ͠·͢
"HFOEB !3 • ୈᶗ෦.JDSP'SPOUFOETͷཧ • ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ • ߟ • ·ͱΊ
"HFOEB !4 • ୈᶗ෦.JDSP'SPOUFOETͷཧ • ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ • ߟ • ·ͱΊ
.JDSP'SPOUFOET͕ղܾ͍ͨ͠ ͦͷͨΊͷٕज़
"HFOEB !5 • ୈᶗ෦.JDSP'SPOUFOETͷཧ • ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ • ߟ • ·ͱΊ
࣮ફ্ͨ͠Ͱͷݟ՝Λڞ༗͠·͢ ຊ൪ೖͷࢀߟʹͳΕʂ
Έͳ͞ΜϚΠΫϩαʔϏε͝ଘͰ͔͢ʁ
ϚΠΫϩαʔϏεͷϝϦοτ͏͚ͯ·͔͢ʁ
Α͋͘ΔϚΠΫϩαʔϏε !8 4FSWFS 4FSWFS $MJFOU 4FSWFS αʔόʔ গਓɾಠཱͨ͠ νʔϜ J04
"OESPJE 8FC
Α͋͘ΔϚΠΫϩαʔϏε !9 4FSWFS 4FSWFS $MJFOU 4FSWFS গਓ ಠཱͨ͠νʔϜ ϚΠΫϩαʔϏε ࠷ߴʂ
J04 "OESPJE 8FC
Α͋͘ΔϚΠΫϩαʔϏε !10 4FSWFS 4FSWFS $MJFOU 4FSWFS গਓ ಠཱͨ͠νʔϜ ϚΠΫϩαʔϏε ࠷ߴʂ
͋Εʁ ϚΠΫϩʁ
ΫϥΠΞϯταΠυ ϚΠΫϩαʔϏεͷϝϦοτ͏͚ͯ·͔͢ʁ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !12 ͱ͋ΔϚΠΫϩαʔϏεͷ։ൃݱ ʢαʔόʔαΠυʣ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !13 ࠷ߴͷاըΛࢥ͍͍ͭͨʂઈରͨΔʂ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !14 ࠷ߴͷاըΛࢥ͍͍ͭͨʂઈରͨΔʂ ͓Ζ͍ʂ͑͑Μʂ͍͖ͬͯʂ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !15 ͍ʂԶ࠷ߴͷاըΛࢥ͍͍ͭͨʂ Զఱ࠽ʂઈରͨΔʂ ͠Βͯ͘͠
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !16 ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !17 ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ ͋ɺͦΕ͚ͬͪͩ͜Ͱ݁͠·͢Ͷʂ Γ·͢ʂ
ͱ͋Δ։ൃݱʢαʔόʔαΠυʣ !18 ͋ɺͦΕ͚ͬͪͩ͜Ͱ݁͠·͢Ͷʂ Γ·͢ʂ ʢϚΠΫϩαʔϏε࠷ߴɻɻɻʣ ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !19 ΫϥΠΞϯταΠυͷ߹
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !20 ࠷ߴͷاըΛࢥ͍͍ͭͨʂઈରͨΔʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !21 ࠷ߴͷاըΛࢥ͍͍ͭͨʂઈରͨΔʂ ͍ʂ ͍͖ͬͯʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !22 ͍ʂԶ࠷ߴͷاըΛࢥ͍͍ͭͨʂ Զఱ࠽ʂઈରͨΔʂ ͠Βͯ͘͠
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !23 ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !24 ΜʔɺӨڹൣғେৎ͔ͳʁ ؾΛ͚ͭͳ͕ΒΓ·͢ʂ ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !25 ΜʔɺӨڹൣғେৎ͔ͳʁ ؾΛ͚ͭͳ͕ΒΓ·͢ʂ ͋ΕʁϓϧϦΫΊͬͪΌίϯϑϦΫτ ͯ͠Δʁʁ ࠷ߴͷاըΛࢥ͍͍ͭͯ͠·ͬͨΜ͚Ͳʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !26 ίϯϑϦΫτ͍ͬ͢ʂʂ ༏ઌॱҐͱQAௐ͠·͢ʂ
ͱ͋Δ։ൃݱʢΫϥΠΞϯταΠυʣ !27 ίϯϑϦΫτ͍ͬ͢ʂʂ ༏ઌॱҐͱQAௐ͠·͢ʂ ͋Εʁ ϢʔβʔʹϦϦʔεͰ͖ͳ͍
ϚΠΫϩαʔϏεͷϝϦοτΛ ेʹ׆͔ͤͯͳ͍ʂ
ΫϥΠΞϯταΠυʹ ϚΠΫϩαʔϏεਫ਼ਆΛʂʂ
ͦ͜Ͱ.JDSP'SPOUFOETʂʂ
ୈᶗ෦.JDSP'SPOUFOETͷཧ
ϚΠΫϩαʔϏεͷ֓ཁ !32 • ʮϚΠΫϩαʔϏεɺڠௐͯ͠ಈ࡞͢Δখ نͰࣗతͳαʔϏεͰ͢ɻʯ • ϏδωεͷڥքͱαʔϏεͷڥքΛἧ͑Δ͜ ͱͰιϑτΣΞͷਝͳఏڙΛ࣮ݱ͠Ϗδ ωεΛՃ͢Δ
ϚΠΫϩαʔϏεͷϝϦοτ !33 • σϓϩΠͷಠཱ • αʔϏε͕͔Ε͍ͯΔͷͰσϓϩΠ͕ࣗ༝ʹͰ͖Δ • ٕज़ҟ࣭ੑ • αʔϏεಛੑ͝ͱʹ࠷దͳٕज़બ͕Մೳ
• ٕज़తෛ࠴ΛҾ͖ͣΓʹ͍͘ • ߹Մೳੑ • αʔϏε͝ͱʹదͳཻͷ"1*͕ެ։͞Ε͍ͯΔͨΊɺ࠶ར༻ੑ͕ߴ·Δ • ৫ͷࣗੑ • ίϛϡχέʔγϣϯύεΛݮΒ͠ɺҙࢥܾఆΛ͘͢͢Δ
ϚΠΫϩαʔϏεͷϝϦοτ !34 • ϙΠϯτߴڽूͱૄ݁߹ •ڽूͷߴ͍αʔϏε͕ૄ݁߹Ͱ͋Δ͜ͱʹΑΓߴ͍ࣗ༝ɾࣗੑ ͕ಘΒΕΔ •ٯʹαʔϏε͕ີ݁߹ʹͳΔͱϚΠΫϩαʔϏεͷྑ͞ফ͑Δ
ϚΠΫϩαʔϏεͷϝϦοτ !35 • ϙΠϯτߴڽूͱૄ݁߹ •ڽूͷߴ͍αʔϏε͕ૄ݁߹Ͱ͋Δ͜ͱʹΑΓߴ͍ࣗ༝ɾࣗੑ ͕ಘΒΕΔ •ٯʹαʔϏε͕ີ݁߹ʹͳΔͱϚΠΫϩαʔϏεͷྑ͞ফ͑Δ ͋ɺͦΕͬͪ͜ͷαʔϏεͰ͢ʂ Γ·͢ʂ ʢϚΠΫϩαʔϏε࠷ߴɻɻɻʣ
ϚΠΫϩαʔϏεͷϝϦοτ !36 • ϙΠϯτߴڽूͱૄ݁߹ •ڽूͷߴ͍αʔϏε͕ૄ݁߹Ͱ͋Δ͜ͱʹΑΓߴ͍ࣗ༝ɾࣗੑ ͕ಘΒΕΔ •ٯʹαʔϏε͕ີ݁߹ʹͳΔͱϚΠΫϩαʔϏεͷྑ͞ফ͑Δ ίϯϑϦΫτ͍ͬ͢ʂʂ
݁߹Λ͍͔ʹԼ͛Δ͔͕ ϚΠΫϩαʔϏεͷ՝
ϚΠΫϩαʔϏεʹ͓͚ΔϑϩϯτΤϯυ !38 • ϚΠΫϩαʔϏεͷ౷߹ϨΠϠʔ •Ϣʔβʔ͕ݟΔͷͭͷ8FCը໘ •6*Ϣʔβʔͷମݧ౷߹͞Ε͍ͯΔ ඞཁ͕͋Δ •݁߹͍͢͠ Side Bar
Contents Header Footer
ϑϩϯτΤϯυϞϊϦε !39 • ϑϩϯτΤϯυϞϊϦε • όοΫΤϯυϚΠΫϩαʔϏεԽͯ͠ ͍ΔͷʹϑϩϯτΤϯυϞϊϦγοΫ ͳঢ়ଶ • ݁߹͕ߴ͘ϚΠΫϩαʔϏεͷྑ͞Λ
ফͯ͠͠·͏
ϑϩϯτΤϯυͷ݁߹Λ Լ͛ΔʹͲ͏͢ΕΑ͍͔ʁ
֤ϚΠΫϩαʔϏε͕ ϑϩϯτΤϯυ·ͰఏڙͰ͖ΕΑ͍
.JDSP'SPOUFOETʂʂ
.JDSP'SPOUFOET !43 • ϑϩϯτΤϯυ·Ͱఏڙ͢ΔϚΠΫϩαʔϏε •֤όοΫΤϯυ+40/ͳͲͷσʔλͰͳ͘6*·ͰҰؾ௨؏ͯ͠ฦ͢
.JDSP'SPOUFOET !44 • ϑϩϯτΤϯυͰαʔϏενʔϜ͝ͱͷ։ൃΛՄೳʹ͢Δ
ͨͱ͑ʁ
.JDSP'SPOUFOETͷྫ !46 • &$αΠτΛྫʹ αϯϓϧNJDSPGSPOUFOETPSHΛ͓आΓ͍ͯ͠·͢ʣ
.JDSP'SPOUFOETͷྫ !47 • .JDSPTFSWJDFTΛಋೖ͍ͯ͠Δ •ϓϩμΫτνʔϜʢҰཡػೳʣ •νΣοΫΞτνʔϜ ߪೖػೳʣ •ϨίϝϯυνʔϜʢ͓͢͢Ίʣ
.JDSP'SPOUFOETͷྫ !48 • ֤νʔϜҟͳΔ,1*Λୡ͍ͨ͠ •ͷΫϦοΫʢϓϩμΫτνʔϜʣ •ߪೖྃʢνΣοΫΞτνʔϜʣ •Ϩίϝϯσʔγϣϯਫ਼ ʢϨίϝϯυνʔϜʣ
.JDSP'SPOUFOETͷྫ !49 • ϑϩϯτΤϯυϞϊϦεͰى͖Δ •σϓϩΠ͕ґଘ͢Δ •ҟͳΔٕज़ͷಋೖ͕ࠔ •৫͕ґଘ͢Δ
.JDSP'SPOUFOETͷྫ !50 • .JDSP'SPOUFOETͷ߹ •֤νʔϜ͕ͦΕͧΕͷίϯϙʔωϯτ·Ͱฦ͢ https://noti.st/naltatis/HxcUfZ/micro-frontends-think-smaller-avoid-the-monolith-love-the-backend#sRI5XtT
.JDSP'SPOUFOETͷྫ !51 • ͭͷը໘ʹରͯ͠ϚΠΫϩαʔϏε͝ͱʹෳίϯϙʔωϯτΛஔ ͢Δ • ౷߹ϨΠϠʔ͕औΓ·ͱΊΔ https://noti.st/naltatis/HxcUfZ/micro-frontends-think-smaller-avoid-the-monolith-love-the-backend#sRI5XtT
.JDSP'SPOUFOETͷྫ !52 • ϝϦοτ •σϓϩΠ͕༰қʹͳΔ •ҟͳΔٕज़࠾༻Մೳ •֤νʔϜͷࣗੑ͕ߴ·Δ
औΓΈͷࣄྫ !53 • औΓΜͰ͍Δاۀ •*,&" •4QPUJGZ •ͳͲ IKEA Spotify https://www.youtube.com/watch?v=4KVOuQDIfmw
https://www.slideshare.net/kevingoldsmith/how-spotify-builds-products-organization-architecture-autonomy-accountability
͑͑Μ
ͰͲ͏࣮ͬͯݱ͢Δͷ͔ʁ
.JDSP'SPOUFOETΛߏ͢Δٕज़ελοΫ !56 • ಉ͡ը໘ʹ֤ϚΠΫϩαʔϏε͕ίϯϙʔωϯτΛஔ͢Δ • JGSBNF •ੲͳ͕Βͷख๏ •Βͳ͍͏ͪʹ͜ΕΛಋೖͯ͠.JDSP'SPOUFOETΛ͍ͬͯΔ Մೳੑ͕͋Δ •
8FC$PNQPOFOUT •৽͍͠8FCͷ༷
8FC$PNQPOFOUTͱ !57 • ʮ8FC$PNQPOFOUTɺ࠶ར༻ՄೳͳΧελϜཁૉΛ࡞͠ɺΣϒ ΞϓϦͷதͰར༻͢ΔͨΊͷɺҰ࿈ͷςΫϊϩδʔͰ͢ɻίʔυͷଞͷ ෦͔Βಠཱͨ͠ɺΧϓηϧԽ͞ΕͨػೳΛ࣮ͬͯݱ͠·͢ɻʯ • $VTUPN&MFNFOUT • $VTUPN&WFOUT
$VTUPN&MFNFOUTͷྫ !58 • ։ൃऀ͕৽͍͠)5.-λάΛ࡞ͨ͠Γɺطଘଞͷσϕϩούʔ͕࡞ ͨ͠ίϯϙʔωϯτΛ֦ுͨ͠Γ͢Δ͜ͱ͕Ͱ͖Δ"1* • ྫ(JU)VC •K2VFSZͰ͍ͭͬͯͨ͘$PNQPOFOUΛ$VTUPN&MFNFOUTͰஔ <relative-time datetime="2018-08-21T21:44:05Z"
title="Aug 22, 2018, 6:44 AM GMT+9"> on Aug 22 </relative-time> https://githubengineering.com/removing-jquery-from-github-frontend/
• ߪങϘλϯΛग़͢ྫ $VTUPN&MFNFOUTͷྫ !59 class BlueBuy extends HTMLElement { constructor()
{ super(); this.innerHTML = ɹɹɹ`<button type="button">buy for 66,00 €</button>`; } disconnectedCallback() { ... } } window.customElements.define('blue-buy', BlueBuy); <blue-buy></blue-buy>
.JDSP'SPOUFOETͷ࣮ݱํ๏ !60 { "id": 1, "name": "foobar", "title": “hooray!” }
JSON { "id": 1, "name": "foobar", "title": “hooray!” } JSON { "id": 1, "name": "foobar", "title": “hooray!” } JSON { "id": 1, "name": "foobar", "title": “hooray!” } JSON • ैདྷόοΫΤϯυ ֤.JDSPTFSWJDFT ͕σʔλΛฦ͢
.JDSP'SPOUFOETͷ࣮ݱํ๏ !61 <div> <h1>hooray!</h1> <p>foobar</p> </div> <div> <h1>hooray!</h1> <p>foobar</p> </div>
<div> <h1>hooray!</h1> <p>foobar</p> </div> <div> <h1>hooray!</h1> <p>foobar</p> </div> HTML • ϑϩϯτΤϯυ ͭͷ6*ʹมͯ͠ ͍ͨ
.JDSP'SPOUFOETͷ࣮ݱํ๏ !62 class BlueBuy extends HTMLElement { constructor() { super();
this.innerHTML = `<button ></button>`; } disconnectedCallback() { ... } } window.customElements.define('b lue-buy', BlueBuy); JavaScript <blue-buy></blue-buy> HTML • 8FC$PNQPOFOUTͷఆٛʢ+4 Λฦ͢ • ౷߹ϨΠϠʔͦΕΛஔ͢Δ͚ͩ
None
.JDSP'SPOUFOETͷཧ·ͱΊ !64 • ϑϩϯτΤϯυϞϊϦεͷ՝Λղܾ͢Δ •σϓϩΠ͕ಠཱͰ͖ͳ͍ •ҟͳΔٕज़ΛબͰ͖ͳ͍ •৫ͷґଘ
.JDSP'SPOUFOETͷཧ·ͱΊ !65 • .JDSP'SPOUFOETϑϩϯτΤϯυ·Ͱఏڙ͢ΔϚΠΫϩαʔϏε
.JDSP'SPOUFOETͷཧ·ͱΊ !66 • ٕज़తʹJGSBNF8FC$PNQPOFOUTΛ࣮ͬͯݱ class BlueBuy extends HTMLElement { constructor()
{ super(); this.innerHTML = `<button ></button>`; } disconnectedCallback() { ... } } window.customElements.define('b lue-buy', BlueBuy); JavaScript <blue-buy></blue-buy> HTML
.JDSP'SPOUFOET ϑϩϯτΤϯυͷ݁߹ΛԼ͛ ϚΠΫϩαʔϏεͷϝϦοτΛ׆͔͢ʂ
.JDSPTFSWJDFT.JDSP'SPOUFOET
None
ձࣾհ !70 •'J/$5FDIOPMPHJFTϔϧεέΞͷϓϥοτϑΥʔϜΛӡӦ͍ͯ͠Δ •'J/$ΞϓϦɺ&$ɺاۀ͚ͷ݈߁ιϦϡʔγϣϯͳͲΛఏڙ •ϔϧεέΞΞϓϦϥϯΩϯά/P
ձࣾհ !71 •ϓϥοτϑΥʔϜΛ࠷Ͱఏڙ͢ΔͨΊʹϚΠΫϩαʔϏεΛऔΓೖΕ ͍ͯΔ •ࠓճ.JDSP'SPOUFOETΛ࣮ફͯ͠ΈͨͷͰݟΛڞ༗͠·͢
ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ
ϏδωεͷഎܠGJODBQQXFC !73 • 'J/$ʹੲ͔ΒGJODBQQXFCͱ͍͏ΞϓϦͷXFC൛͕͋Δ •4JOHMF1BHF"QQMJDBUJPO 41" Ͱ3FBDUKT •͙Β͍લ͔Β͋ΔׂͱϨΨγʔͳϑϩϯτΤϯυ
ϏδωεͷഎܠϐΞɾϘʔφεػೳ !74 • ࣾһʹࠒͷײँΛ͑Δ͓ྱϙΠϯτ༩ػೳΛࣾπʔϧͱͯ͠࡞ ͢Δ͜ͱͱͳͬͨ
ϏδωεͷഎܠϐΞɾϘʔφεػೳ !75 • ϔϧεέΞͷϓϥοτϑΥʔϜΛࢦ͢'J/$ͱͯࣾ͠πʔϧ ͱ͍͑ɺϐΞɾϘʔφεػೳGJODBQQXFC্ʹͷ͓͖͍ͤͯͨ • ී௨ʹ3FBDUΛ࣮͢Δ͜ͱߟ͑ΒΕΔ͕ɺ.JDSP'SPOUFOETΛ࠾༻ ͨ͠
ͳͥ.JDSP'SPOUFOETΛ࠾༻͔ͨ͠ !76 • ී௨ʹ3FBDUͰ͍͍ͷͰʁ •όοΫΤϯυ3BJMTͰ৽্ཱͪ͛͘͠Δ •νʔϜͷٕज़ελοΫతͱͯ͠3FBDUPO3BJMTͷϊϋ͕ཷ·ͬͯ ͍ͨ •ϨΨγʔͳGJODBQQXFCͷίʔυϕʔεΛ͋·Γେ͖ͨ͘͘͠ͳ͔ͬͨ •ͭ·Γ৽چ3FBDUͱ͍͏ٕज़ҟ࣭ੑ͕͋Δঢ়ଶ
ͳͥ.JDSP'SPOUFOETΛ࠾༻͔ͨ͠ !77 • Ұ.JDSP'SPOUFOETͷج൫͕͑ •ࠓޙ͍ΖΜͳαʔϏε͕ίϯϙʔωϯτΛఏڙͰ͖Δ •ඞཁʹԠͯ͡কདྷ؆୯ʹSFQMBDF͢Δ͜ͱͰ͖Δ •֤αʔϏε͝ͱʹಠཱͯ͠վम͕Ͱ͖Δ • ٕज़ҟ࣭ੑ߹Մೳੑ͕͋Δͱ͜ΖʹϝϦοτ͕͋Δ .JDSP'SPOUFOETΛ࠾༻
։ൃதͷը໘ !78 • ։ൃதͷ࣮ࡍͷը໘ͱαϯϓϧίʔυ
ίʔυΠϝʔδ !79 • ϐΞɾϘʔφεଆ class TimelineComponents extends HTMLElement { async
connectedCallback() { const mountPoint = document.createElement('span'); const shadow = this.attachShadow({ mode: 'open' }); shadow.appendChild(mountPoint); const currentUser = await UserRepository.fetch(); render( <React.Fragment> <link rel="stylesheet" type="text/css" href="http://localhost:3020/main.css"/> <Timeline currentUser={currentUser}/> </React.Fragment>, mountPoint, ); retargetEvents(shadow); } } customElements.define(‘peerbonus—timeline’, TimelineComponents);
ίʔυΠϝʔδ !80 • GJODBQQXFCଆ import React from 'react'; class PeerBonus
extends React.Component { render() { return ( <div className="l-main_contents_area"> <peerbonus-timeline></peerbonus-timeline> </div> ); } } export default PeerBonus;
࣮ફʹ͓͍ͯ໘ͨ͠՝ !81 • ࣮ࡍʹͲͷΑ͏ͳʹ͍ͭͯऔΓΜͩͷ͔Λհ͠·͢ • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝
• άϩʔόϧͳঢ়ଶͷڞ༗ • ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
DBDIFCVTUJOHͷ !82 • Ϗϧυͷ •DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ • άϩʔόϧͳঢ়ଶͷڞ༗ •
ը໘ભҠ • ঢ়ଶཧ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !83 • 41"Ϗϧυ͕ඞཁ • ผͷϑΝΠϧΛJNQPSU͢Δ༷͕࠷ۙ·Ͱͳ͔ͬͨͷͰɺϥΠϒϥϦ Ͱ͕ΜͬͯCVOEMFͯ͠ຕʹ͍ͯ͠Δ •Ұ൪࢝ΊʹಡΈࠐ·ΕΔIUNM JOEFYIUNM
ͰϏϧυͨ͠ϑΝΠϧΛࢦఆ ͯ͠ಡΈࠐΉΑ͏ʹ͢Δ ϒϥβ HTML JS index.html bundle.js JS JS JS Ϗϧυ styleλά
എܠ !84 • Ϗϧυ͢Δͨͼʹҧ͏ϑΝΠϧ໊Ͱఏڙ͢Δඞཁ͕͋Δɻ •ಉҰϑΝΠϧ໊ͩͱϒϥβͷΩϟογϡ͕ޮ͍ͯมߋ͕ө͞Εͳ͍ ͜ͱ͕͋Δ •ϦϦʔε࣌ʹࠔΔ ϒϥβ HTML JS
index.html new bundle.js JS JS JS Ϗϧυ styleλά JS cached bundle.js ΩϟογϡΛࢀর
എܠ !85 • ௨ৗϑΝΠϧ໊ʹϥϯμϜͳจࣈྻʢDIVOLIBTI Λ͚ͭͯɺϏϧυ͝ͱ ʹҟͳΔϑΝΠϧ໊ʹͳΔΑ͏ʹ͢Δ ϒϥβ HTML JS index.html
new bundle-ac2c5.js JS JS JS Ϗϧυ styleλά JS cached bundle-c1df8.js ࢀর͞Εͳ͍
՝ !86 • ͭͷαʔϏεͰϏϧυJOEFYIUNMͷఏڙ͍ͯ͠Δͱ͖ͳ͍ •DIVOLIBTI͕Θ͔ΔͷͰɺJOEFYIUNMଆͰͦΕΛࢦఆͯ͠Δ͚ͩ HTML JS index.html bundle-ac2c5.js <script
src=“./bundle- ac2c5.js”></script>
՝ !87 • .JDSP'SPOUFOETͷ߹ •αʔϏε͕6*ఏڙଆʢJOEFYIUNMଆ ͱϏϧυଆͰΘ͔Ε͍ͯΔ •Ϗϧυ͝ͱʹDIVOLIBTI͖ͷϑΝΠϧ໊Λڞ༗͢Δͷ͕͍͠ HTML JS index.html
bundle-ac2c5.js <script src=“./bundle-?????.js”> </script>
ݕ౼ !88 • NBOJGFTUKTPOΛ͑औಘͰ͖Δ •ϑΝΠϧ໊ͱDIVOLIBTI͖ϑΝΠϧ໊ͷؔ࿈Λද͢KTPOϑΝΠϧ •ϑΝΠϧ໊ΛLFZͱ࣮ͯ͠ࡍͷDIVOLIBTI͖ͷϑΝΠϧ໊ΛͨͲΔ͜ ͱ͕Ͱ͖Δ HTML JS index.html
bundle-ac2c5.js { "bundle.js": "bundle-ac2c5.js" } <script src=“./bundle-ac2c5.js”> </script> manifest.json
ରԠ !89 • NBOJGFTUKTPOΛͬͯGJMF໊Λऔಘ͢Δ • औಘͷλΠϛϯάJOEFYIUNMΛฦ͢લʹαʔόʔαΠυͰߦ͏ •TDSJQUλάΛຒΊࠐΜͩঢ়ଶͰϒϥβʹฦ͢
άϩʔόϧͳঢ়ଶͷڞ༗ !90 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ •άϩʔόϧͳঢ়ଶͷڞ༗ •
ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !91 • άϩʔόϧʹڞ༗͍ͨ͠ঢ়ଶ͕ଘࡏ͢Δ • ೝূͷঢ়ଶ • Ϣʔβʔجຊใ • FUD
՝ !92 • αʔϏεͷૄ݁߹Λอͬͨ··Ͳ͏ڞ༗͢Δ͔ • Ξϯνύλʔϯڞ༗σʔλετΞ • ڊେͳڞ༗"1*ʹͳΔ • มߋ͕ࠔ
ೝূ Micro Frontends Micro Frontends localStorage Write Read Ξϯνύλʔϯ: ڞ༗σʔλετΞ
ݕ౼௨ৗͷ41"ͷ߹ 'MVY !93 ೝূ Component Component Store Dispatcher notify state
change change state Action (loggedIn)
ରԠ.JDSP'SPOUFOETͷ߹ !94 ೝূ Micro Frontends Micro Frontends Store Dispatcher change
state ౷߹ϨΠϠʔ Micro Frontends Custom Events (globalStateChanged) Custom Events (loggedIn)
ରԠ.JDSP'SPOUFOETͷ߹ !95 ೝূ Micro Frontends Micro Frontends Store Dispatcher change
state ౷߹ϨΠϠʔ Micro Frontends Custom Events (globalStateChanged) Custom Events (loggedIn) 'MVYͷΞφϩδʔͰղܾͰ͖Δʂ
ը໘ભҠ !96 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ • άϩʔόϧͳঢ়ଶͷڞ༗
•ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !97 • ϐΞɾϘʔφεը໘͔ΒͦΕҎ֎ͷը໘ʹભҠ͍ͨ͠ •۩ମతʹϢʔβʔͷϓϩϑΟʔϧͳͲΛطଘͷը໘ʹભҠ͍ͤͨ͞
՝ !98 • ී௨ʹBλάͰભҠͯ͠͠·͏ͱ41"ͱͯ͠ͷભҠ͕Ͱ͖ͳ͘ͳͬͯ ͠·͏ •IJTUPSZBQJͰભҠͤ͞Δඞཁ͕͋Δ
՝ !99 • ֤αʔϏε͕ࣗ༝ʹભҠͤ͞ΔͱΧΦε •XJOEPXMPDBUJPOͱ͔XJOEPXIJTUPSZͱ͔͏ͱࣗ༝ʹը໘ભҠͰ͖Δ •ΞϓϦέʔγϣϯ֎෦ભҠ͢Δ͜ͱͰ͖ΔΑ͏ʹͳΔ • ը໘ભҠ౷߹ϨΠϠʔͷͱͯ͠ཧͨ͠΄͏͕͍͍
ཹҙ !100 • 8FCͷମݧͱἧ͑Δ •BλάΛ͏ •ӈΫϦοΫͰλϒ͕։͚Δඞཁ͕͋Δ •CVUUPOλάΛ͏ͱ8FCͷମݧͱ߹͠ͳ͍ʂ
ରԠ !101 • ී௨ʹBλάΛ͏ • IJTUPSZBQJͰભҠ͢ΔͨΊʹ౷߹ϨΠϠʔʹભҠઌΛFWFOUΛ͛Δ • ౷߹ϨΠϠʔFWFOU͔ΒભҠઌͷใΛड͚औͬͯը໘ભҠ͢Δ
ରԠ !102 • ࣮ΠϝʔδϦϯΫ༻ͷίϯϙʔωϯτ class FawLink extends Component { handleClick
= (e) => { const { to } = this.props; e.preventDefault(); const event = new CustomEvent('transition', { detail: { to, }, }); window.dispatchEvent(event); }; render() { const { to, children } = this.props; return ( <a href={to} onClick={this.handleClick}> {children} </a> ); } } export default FawLink;
ରԠ !103 • ࣮ΠϝʔδϦϯΫ༻ͷίϯϙʔωϯτ class FawLink extends Component { handleClick
= (e) => { const { to } = this.props; e.preventDefault(); const event = new CustomEvent('transition', { detail: { to, }, }); window.dispatchEvent(event); }; render() { const { to, children } = this.props; return ( <a href={to} onClick={this.handleClick}> {children} </a> ); } } export default FawLink; BλάͰఆٛ
ରԠ !104 • ࣮ΠϝʔδϦϯΫ༻ͷίϯϙʔωϯτ class FawLink extends Component { handleClick
= (e) => { const { to } = this.props; e.preventDefault(); const event = new CustomEvent('transition', { detail: { to, }, }); window.dispatchEvent(event); }; render() { const { to, children } = this.props; return ( <a href={to} onClick={this.handleClick}> {children} </a> ); } } export default FawLink; $VTUPN&WFOUΛఆٛ͠ ͯ͛Δ
ରԠ !105 • ࣮Πϝʔδ౷߹ϨΠϠʔଆ import React from 'react'; import {
browserHistory } from 'react-router'; class Peerbonus extends React.Component{ componentDidMount() { window.addEventListener('transition', (e) => { browserHistory.push(e.detail.to); }); } render() { return ( <div className="l-main_contents_area"> <peerbonus-timeline></peerbonus-timeline> </div> ); } } export default Peerbonus; &WFOU͔ΒભҠઌΛ ड͚औͬͯભҠ
ঢ়ଶཧ !106 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ • άϩʔόϧͳঢ়ଶͷڞ༗
• ը໘ભҠ •ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !107 • ࿈ಈ͢Δͭͷίϯϙʔωϯτ͕͋Δ ߘϘλϯΛԡ͢ͱมߋ͕ө͞ΕΔ
എܠ !108 • ௨ৗͷ41"ͷઃܭύλʔϯͩͱຕͷΦϒδΣΫτΛ4JOHMFTPVSDFPG USVUIͱͯ͠ঢ়ଶཧ͢Δ 3FEVYͷHMPCBM4UBUFͳͲ { givenPoints: 200, sendablePoints:
400, }
ݕ౼ !109 • .JDSP'SPOUFOETʹ͓͚Δίϯϙʔωϯτؒͷ࿈ܞύλʔϯ •ύλʔϯαʔϏεͷͱ͖ •ύλʔϯෳαʔϏεΛ·͕ͨΔͱ͖
ݕ౼αʔϏεͷͱ͖ !110 • ͭͷαʔϏε͕ෳͷ$PNQPOFOUTΛฦ͢ • ͦΕΒ͕࿈ಈͯ͠ಈ͘
ݕ౼αʔϏεͷͱ͖ !111 • ಉҰαʔϏεͳͷͰͭͷHMPCBM4UBUFʹͭͳ͕ͬͨܗͰఏڙ͢Δ͜ͱ͕ Մೳ Component1 Component2 { state1: 1,
state2: 2, }
ݕ౼ෳαʔϏεΛ·͕ͨΔͱ͖ !112 • $VTUPN&WFOUTΛ͏ •ίϯϙʔωϯτFWFOUΛͳ͛Δ •౷߹ϨΠϠʔΠϕϯτΛ͛͢ •ଞͷίϯϙʔωϯτʹ௨͞ΕΔ Component1 Component2 CustomEvents
ରԠ !113 • ಉҰαʔϏεͳͷͰͭͷHMPCBM4UBUFʹͭͳ͕ͬͨܗͰఏڙ͢Δ const store = configureStore(); class XCountButton
extends HTMLElement { connectedCallback() { const mountPoint = document.createElement('span'); const shadow = this.attachShadow({ mode: 'open' }) shadow.appendChild(mountPoint); render( <Provider store={store}> <CountButton /> </Provider>, mountPoint ); retargetEvents(shadow) } } customElements.define('x-count-button', XCountButton); class XCountDisplay extends HTMLElement { connectedCallback() { const mountPoint = document.createElement('span'); this.attachShadow({ mode: 'open' }).appendChild(mountPoint); render( <Provider store={store}> <CountDisplay /> </Provider>, mountPoint ); } } customElements.define('x-count-display', XCountDisplay);
ରԠ !114 • ڞ௨ͷTUPSFΛఆٛ͠QSPWJEFSʹ͢ const store = configureStore(); class XCountButton
extends HTMLElement { connectedCallback() { const mountPoint = document.createElement('span'); const shadow = this.attachShadow({ mode: 'open' }) shadow.appendChild(mountPoint); render( <Provider store={store}> <CountButton /> </Provider>, mountPoint ); retargetEvents(shadow) } } customElements.define('x-count-button', XCountButton); class XCountDisplay extends HTMLElement { connectedCallback() { const mountPoint = document.createElement('span'); this.attachShadow({ mode: 'open' }).appendChild(mountPoint); render( <Provider store={store}> <CountDisplay /> </Provider>, mountPoint ); } } customElements.define('x-count-display', XCountDisplay);
ϒϥβؒͷࠩͷղফ QPMZGJMM !115 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ •
άϩʔόϧͳঢ়ଶͷڞ༗ • ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ •ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !116 • 8FCαʔϏεͱͯ͠ఏڙ͢ΔͷͰɺͰ͖Δ͚ͩଟ͘ͷϒϥβʹରԠ͠ ͍ͨ designed by Pixel perfect from
Flaticon https://www.flaticon.com/packs/browsers
!117 • 8FC$PNQPOFOUTͷϒϥβͷରԠঢ়گʹ͕ࠩ͋Δ • *&ͳͲҰ෦ͷݹ͍ϒϥβαϙʔτ͍ͯ͠ͳ͍ •๏ਓͷ͓٬༷ʹॏཁ https://www.webcomponents.org/
ରԠ !118 • QPMZGJMMΛ͍Εͨ •IUUQTHJUIVCDPNXFCDPNQPOFOUTXFCDPNQPOFOUTKT • QPMZGJMMͱɺ࠷ۙͷػೳΛαϙʔτ͍ͯ͠ͳ͍ݹ͍ϒϥβʔͰɺͦ ͷػೳΛ͑ΔΑ͏ʹ͢ΔͨΊͷίʔυͰ͢ɻେΣϒ্ͷ +BWB4DSJQUͰ͢ɻ
• $VTUPN&MFNFOUTಛʹͳ͘ಈ͘ •ͨͩ͠4IBEPX%0.͚ͩҰ෦ະରԠ
3FBDUͰTIBEPX%0.Λ͔ͭ͏ !119 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ • άϩʔόϧͳঢ়ଶͷڞ༗
• ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍
എܠ !120 • 4IBEPX%0. •%0.ͷΧϓηϧԽΛߦ͏͜ͱ͕Ͱ͖Δ8FC$PNQPOFOUTͷ༷ͷͭ •$44͕֎෦͔ΒִͰ͖ͨΓͯ͠ศར • 3FBDUͱಉ࣌ʹ͑ͳ͍
!121 • 3FBDUΛ4IBEPX%0.ͰXSBQͨ͠ͱ͖ʹ$MJDLFWFOU͕Ԡ͠ͳ͘ͳΔ
ݕ౼ !122 • 3FBDUͷFWFOUͷϋϯυϦϯάํ๏ʹෛ࠴͕͋Δ • *TTVFཱ͍ͬͯΔɺ3FBDU'JSFͱ͍͏ෛ࠴ղফϓϩδΣΫτʹ Ҋͱͯ͠ొ͞Ε͍ͯΔ
ରԠ !123 • ϥΠϒϥϦͰճආՄೳ •SFBDUTIBEPXEPNSFUBSHFUFWFOUT • 3FBDUຊମͷվम·ͪʣ • 1PMZGJMMͷ͜ͱߟ͑Δͱ·ͩૣͦ͏ •
ଞʹIUUQTHJUIVCDPNTLBUFKTWBMͳͲ͕͋ΔΑ͏ͳͷͰৄ͍͠ํ ͍ͨΒڭ͍͑ͯͩ͘͞ʂ
None
ߟ !125 • .JDSP'SPOUFOET࣮ઓೖՄೳ •αʔϏεόοΫΤϯυ͔Β6*·Ͱ୲Ͱ͖ΔͷͰαʔϏεͷϩδοΫ ͕ࢄ͠ͳ͍ •վળ͕༰қ •࠶ར༻ੑ͕ߴ·Δ • ։ൃಠཱͯ͠ϦϦʔεͰ͖ΔͷͰνʔϜͷࣗੑ͕ߴ·Δ
ߟ !126 • #''ͱͷҧ͍ •ू͍ͯ͠ΔϨΠϠʔ͕ҧ͏
ߟ !127 4FSWFS Backend Frontend αʔόʔͰ)5.-Ϩϯμ Ϧϯά͍ͯͨ͠
ߟ !128 Backend Frontend $MJFOU 4FSWFS ΫϥΠΞϯτ͕Ϧονʹ ͳΓ
ߟ !129 Backend Frontend $MJFOU 4FSWFS 4FSWFS 4FSWFS αʔϏε͕ෳࡶԽ͠ɺ ϚΠΫϩʔαʔϏεʹ
ߟ !130 Backend Frontend $MJFOU 4FSWFS 4FSWFS 4FSWFS ϦΫΤετ͕૿͑Δ αʔϏε͝ͱʹҟͳΔΦϒ
δΣΫτ #''
ߟ !131 Backend Frontend $MJFOU 4FSWFS 4FSWFS 4FSWFS #'' ϦΫΤετ͕૿͑Δ
αʔϏε͝ͱʹҟͳΔΦϒ δΣΫτ #''
ߟ !132 Backend Frontend 4FSWFS 4FSWFS 4FSWFS ϑϩϯτΤϯυͷෳࡶ͞ʹ ରॲ.JDSP'SPOUFOET
ߟࠓޙͷ !133 • ύϑΥʔϚϯε͕Γͦ͏ •443 4FSWFSTJEFSFOEFSJOH ͋ͬͨ΄͏͕͍͍ •͜Ε·Ͱͷ8FCͷݟͰഓΘΕͨύϑΥʔϚϯεɾνϡʔχϯά ඞཁ •
6*ͷ౷Ұ • ςετ •%0.#BTFE$%$ $POTVNFSESJWFODPOUSBDU UFTUJOH
·ͱΊ !134 • .JDSP'SPOUFOET.JDSPTFSWJDFTͷຊདྷͷྑ͞Λൃش͠ɺϑϩϯτΤϯ υͷ݁߹ੑΛԼ͛Δ • Ұॹʹݚڀͯ͘͠ΕΔؒΛ୳ͯ͠·͢ •ҰॹʹϚΠΫϩϑϩϯτΤϯυΛΓ͍ͨํʢࣾһɾۀҕୗɾ෭ۀ Մʣɺ%.͍ͩ͘͞ʂ5XJUUFS!OPCVIJLPTBXBJ
ࣗݾհ !135 • ໊લ: ᖒҪ એ ʢ͞Θ͍ ͷͿͻ͜ʣ • twitter/github:
nobuhikosawai • αʔόʔαΠυ(Rails) • ϑϩϯτΤϯυ(React.js)
ࣗݾհ !136 • .JDSPTFSWJDFT.FFUVQ7PMͱ7PMͰ .JDSP'SPOUFOETΛςʔϚʹ͠· ͨ͠ • Α͚ΕฐࣾͷCMPHΈ͍ͯͩ͘͞ https://medium.com/finc-engineering/microservices-meetup-vol-7-bbceaf1b860c https://medium.com/finc-engineering/microservices-meetup-vol-9-44b7664756ee
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠
ࢀߟจݙ !138 • ϚΠΫϩαʔϏεΞʔΩςΫνϟʢ 4BN/FXNBOஶɺࠤ౻ੜमɺԼ༁ʣ • NJDSPGSPOFOETPSH • $PQZSJHIU $
OFVMBOE#ÛSPGÛS*OGPSNBUJL #SFNFO (FSNBOZ IUUQTHJUIVCDPNOFVMBOENJDSPGSPOUFOETMJDFOTF • 3FBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT