Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Micro Frontends の理論と実践 -価値提供を高速化する真のマイクロサービスのあ...
Search
nobuhikosawai
November 02, 2018
Technology
17
39k
Micro Frontends の理論と実践 -価値提供を高速化する真のマイクロサービスのあり方- / The Theory and Practice of Micro Frontends
AWS Dev Day Tokyo 2018でMicro Frontendsについて話したスライドです。
#MicroFrontends #マイクロフロントエンド
nobuhikosawai
November 02, 2018
Tweet
Share
More Decks by nobuhikosawai
See All by nobuhikosawai
GraphQLを用いたサイトに おけるパフォーマンス改善 (ECサイトを題材に)/ Improving online shopping site performance which using the GraphQL
nobuhikosawai
3
5.6k
Micro Frontends (example from micro-frontends.org)
nobuhikosawai
0
920
Railsのタイムゾーン
nobuhikosawai
4
2.2k
Other Decks in Technology
See All in Technology
OS 標準のデザインシステムを超えて - より柔軟な Flutter テーマ管理 | FlutterKaigi 2024
ronnnnn
0
170
Flutterによる 効率的なAndroid・iOS・Webアプリケーション開発の事例
recruitengineers
PRO
0
110
10XにおけるData Contractの導入について: Data Contract事例共有会
10xinc
6
650
TypeScript、上達の瞬間
sadnessojisan
46
13k
飲食店データの分析事例とそれを支えるデータ基盤
kimujun
0
120
[CV勉強会@関東 ECCV2024 読み会] オンラインマッピング x トラッキング MapTracker: Tracking with Strided Memory Fusion for Consistent Vector HD Mapping (Chen+, ECCV24)
abemii
0
220
なぜ今 AI Agent なのか _近藤憲児
kenjikondobai
4
1.4k
ノーコードデータ分析ツールで体験する時系列データ分析超入門
negi111111
0
410
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
160
Amazon Personalizeのレコメンドシステム構築、実際何するの?〜大体10分で具体的なイメージをつかむ〜
kniino
1
100
OCI 運用監視サービス 概要
oracle4engineer
PRO
0
4.8k
100 名超が参加した日経グループ横断の競技型 AWS 学習イベント「Nikkei Group AWS GameDay」の紹介/mediajaws202411
nikkei_engineer_recruiting
1
170
Featured
See All Featured
The Art of Programming - Codeland 2020
erikaheidi
52
13k
Producing Creativity
orderedlist
PRO
341
39k
The Invisible Side of Design
smashingmag
298
50k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
How to Ace a Technical Interview
jacobian
276
23k
GitHub's CSS Performance
jonrohan
1030
460k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
93
16k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
655
59k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
42
9.2k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
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