Share
Embed
Micro Frontends の理論と実践 -価値提供を高速化する真のマイクロサービスのあり方- / The Theory and Practice of Micro Frontends
AWS Dev Day Tokyo 2018でMicro Frontendsについて話したスライドです。
#MicroFrontends #マイクロフロントエンド
nobuhikosawai
November 02, 2018
Transcript
138 slides
-
"HFOEB !4 • ୈᶗ෦.JDSP'SPOUFOETͷཧ • ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ • ߟ • ·ͱΊ
.JDSP'SPOUFOET͕ղܾ͍ͨ͠ ͦͷͨΊͷٕज़ -
"HFOEB !5 • ୈᶗ෦.JDSP'SPOUFOETͷཧ • ୈᶘ෦.JDSP'SPOUFOETͷ࣮ફ • ߟ • ·ͱΊ
࣮ફ্ͨ͠Ͱͷݟ՝Λڞ༗͠·͢ ຊ൪ೖͷࢀߟʹͳΕʂ -
ϚΠΫϩαʔϏεͷϝϦοτ !33 • σϓϩΠͷಠཱ • αʔϏε͕͔Ε͍ͯΔͷͰσϓϩΠ͕ࣗ༝ʹͰ͖Δ • ٕज़ҟ࣭ੑ • αʔϏεಛੑ͝ͱʹ࠷దͳٕज़બ͕Մೳ
• ٕज़తෛ࠴ΛҾ͖ͣΓʹ͍͘ • ߹Մೳੑ • αʔϏε͝ͱʹదͳཻͷ"1*͕ެ։͞Ε͍ͯΔͨΊɺ࠶ར༻ੑ͕ߴ·Δ • ৫ͷࣗੑ • ίϛϡχέʔγϣϯύεΛݮΒ͠ɺҙࢥܾఆΛ͘͢͢Δ -
औΓΈͷࣄྫ !53 • औΓΜͰ͍Δاۀ •*,&" •4QPUJGZ •ͳͲ IKEA Spotify https://www.youtube.com/watch?v=4KVOuQDIfmw
https://www.slideshare.net/kevingoldsmith/how-spotify-builds-products-organization-architecture-autonomy-accountability -
$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 Λฦ͢ • ౷߹ϨΠϠʔͦΕΛஔ͢Δ͚ͩ -
.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 -
ίʔυΠϝʔδ !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 -
άϩʔόϧͳঢ়ଶͷڞ༗ !90 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ •άϩʔόϧͳঢ়ଶͷڞ༗ •
ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍ -
՝ !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.Ͱ͏·͘ಈ͔ͳ͍ -
ରԠ !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.Ͱ͏·͘ಈ͔ͳ͍ -
ରԠ !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 -
ରԠ !118 • QPMZGJMMΛ͍Εͨ •IUUQTHJUIVCDPNXFCDPNQPOFOUTXFCDPNQPOFOUTKT • QPMZGJMMͱɺ࠷ۙͷػೳΛαϙʔτ͍ͯ͠ͳ͍ݹ͍ϒϥβʔͰɺͦ ͷػೳΛ͑ΔΑ͏ʹ͢ΔͨΊͷίʔυͰ͢ɻେΣϒ্ͷ +BWB4DSJQUͰ͢ɻ
• $VTUPN&MFNFOUTಛʹͳ͘ಈ͘ •ͨͩ͠4IBEPX%0.͚ͩҰ෦ະରԠ -
3FBDUͰTIBEPX%0.Λ͔ͭ͏ !119 • Ϗϧυͷ • DBDIFCVTUJOH • Ϣʔβʔମݧ౷߹ͷ՝ • άϩʔόϧͳঢ়ଶͷڞ༗
• ը໘ભҠ • ίϯϙʔωϯτؒͷ࿈ܞ • ϥΠϒϥϦͷ • ϒϥβؒͷࠩͷղফ QPMZGJMM • 3FBDUͷFWFOU͕TIBEPX%0.Ͱ͏·͘ಈ͔ͳ͍ -
ରԠ !123 • ϥΠϒϥϦͰճආՄೳ •SFBDUTIBEPXEPNSFUBSHFUFWFOUT • 3FBDUຊମͷվम·ͪʣ • 1PMZGJMMͷ͜ͱߟ͑Δͱ·ͩૣͦ͏ •
ଞʹIUUQTHJUIVCDPNTLBUFKTWBMͳͲ͕͋ΔΑ͏ͳͷͰৄ͍͠ํ ͍ͨΒڭ͍͑ͯͩ͘͞ʂ -
ߟ !131 Backend Frontend $MJFOU 4FSWFS 4FSWFS 4FSWFS #'' ϦΫΤετ͕૿͑Δ
αʔϏε͝ͱʹҟͳΔΦϒ δΣΫτ #'' -
ߟࠓޙͷ !133 • ύϑΥʔϚϯε͕Γͦ͏ •443 4FSWFSTJEFSFOEFSJOH ͋ͬͨ΄͏͕͍͍ •͜Ε·Ͱͷ8FCͷݟͰഓΘΕͨύϑΥʔϚϯεɾνϡʔχϯά ඞཁ •
6*ͷ౷Ұ • ςετ •%0.#BTFE$%$ $POTVNFSESJWFODPOUSBDU UFTUJOH -
ࣗݾհ !135 • ໊લ: ᖒҪ એ ʢ͞Θ͍ ͷͿͻ͜ʣ • twitter/github:
nobuhikosawai • αʔόʔαΠυ(Rails) • ϑϩϯτΤϯυ(React.js) -
ࢀߟจݙ !138 • ϚΠΫϩαʔϏεΞʔΩςΫνϟʢ 4BN/FXNBOஶɺࠤ౻ੜमɺԼ༁ʣ • NJDSPGSPOFOETPSH • $PQZSJHIU $
OFVMBOE#ÛSPGÛS*OGPSNBUJL #SFNFO (FSNBOZ IUUQTHJUIVCDPNOFVMBOENJDSPGSPOUFOETMJDFOTF • 3FBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT • IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT