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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
nobuhikosawai
November 02, 2018
Technology
40k
17
Share
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
GitHub Copilot CLIでWebアクセシビリティを改善した話
tomokusaba
0
140
インフラが苦手でも大丈夫! 紙芝居 Kubernetes -WWGT 10周年編-
aoi1
1
310
AIガバナンス実践 - 生成AIコネクタのデータ漏洩リスクと実務対策
knishioka
0
150
AI Adaptable なテストを整える工夫 / Ways to Make Your Tests AI-Adaptable
bitkey
PRO
2
190
AIプラットフォームを運用し続けるための可観測性
tanimuyk
4
730
大学生が本気でDatabricksを活用してDiscordサークルをデータ駆動させてみた
phantomjuju
1
300
Claude Codeを組織で使いこなす— サーバサイドAIエージェント運用の実践知
techtekt
PRO
0
130
JJUG CCC 2026 Spring AI時代の開発こそ標準化を武器に! ― 方式・プロセス・プラットフォームの標準化
s27watanabe
2
640
『家族アルバム みてね』における インシデント対応との向き合い方 / Approach incident response in Family Album
kohbis
2
280
はじめてのDatadog
kairim0
0
240
個人AIからチームAIへ:開発における品質と生産性の再設計
moongift
PRO
0
320
美味しいスイスチーズを作ろう🧀🐭
taigamikami
1
190
Featured
See All Featured
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
Building the Perfect Custom Keyboard
takai
2
780
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
940
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
130
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Ruling the World: When Life Gets Gamed
codingconduct
0
240
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
180
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Product Roadmaps are Hard
iamctodd
PRO
55
12k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
150
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