MICROSERVICE
WEBSITES
Gustaf Nilsson Kotte
@gustaf_nk
Mobile devices
The problem
How can we develop a website with multiple teams?
Different business units need to make a website together
that should feel like “one website” for the end-user.
Example: Retail domain
Products, Shopping cart, Checkout, Recommendations, Reviews,
User profile, Editorial content, etc...
The problem
Decentralized services but centralized web?
Frontend
Bottleneck
Services should have their own frontend!
Stefan Tilkov et al, “Self-Contained Systems”
Frontend Frontend FrontendFrontend
Enables continuous delivery
Decentralized governance
An option for teams to choose different tools
→ heterogeneous/plural/diverse system
High rate of change on the frontend
Allows the system to evolve over time
James Lewis & Martin Fowler, “Microservices: Decentralized Governance”
Addy Osmani, “JavaScript Start-up Performance”
Mobile device performance
Constraints (need to support)
Continuous delivery
Decentralized governance
Good performance on mobile devices
Microservice Websites
Mobile devices
Analysis
INTEGRATION USE CASE
Shopping cart
How can we reuse the shopping cart between
different parts of the web site?
“Many-to-one”
Multiple teams – one shopping cart
INTEGRATION USE CASE
Landing page
The web site’s landing page is the page with the
most amount of traffic and many business units
want to be represented there.
“One-to-many”
Multiple teams – one landing page
Integrating on...
What (data/code/content)
When (build/run)
Where (client/server)
Cartesian product → 12 combinations
Let’s go through them!
Analysis
What × when × where
What? When? Where?
Data (APIs) Build Client
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client
Server
Run Client
Server
Content (HTML) Build Client
Server
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client
Server
Run Client
Server
Content (HTML) Build Client
Server
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance.
Server
Run Client
Server
Content (HTML) Build Client
Server
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance.
Server
Run Client No diversity + mobile performance.
Server N/A
Content (HTML) Build Client
Server
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance.
Server
Run Client No diversity + mobile performance.
Server N/A
Content (HTML) Build Client
Server
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance
Server
Run Client No diversity + mobile performance.
Server N/A
Content (HTML) Build Client N/A
Server “Static page fragments” – ok, but limited amount of use cases
Run Client
Server
What × when × where
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance.
Server
Run Client No diversity + mobile performance.
Server N/A
Content (HTML) Build Client N/A
Server “Static page fragments” – ok, but limited amount of use cases
Run Client Client-side transclusion
Server Server-side transclusion
What × when × where
What? When? Where?
Data (APIs) Build Client No reuse at all, only API usage
Server
Run Client
Server
Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance.
Server
Run Client No diversity + mobile performance.
Server N/A
Content (HTML) Build Client N/A
Server “Static page fragments” – ok, but limited amount of use cases
Run Client Client-side transclusion
Server Server-side transclusion
What × when × whereWhat × when × where
Mobile devices
Transclusion
Transclusion
“transclusion is the inclusion of part or all of an electronic document
into one or more other documents by hypertext reference” (Wikipedia: Transclusion)
Producer
Expose a fragment resource, "/retail/shopping-cart/"
Consumer
Include the fragment declaratively, similar to <img src="..." />
Transclusion can be used both on server and client
Examples: Edge-Side Includes (server) and <h-include> (client)
No relative URLs in fragments!
<img src="images/relative-does-not-work.png" />
<img src="/images/root-relative-often-works.png" />
<img src="https://image-resources.jayway.com/images/absolute-always-works.png" />
...because URLs are relative to the consumer’s context (not the producer’s)
Edge Side Includes (server)
<-- Include the shopping cart component -->
<esi:include src="/retail/shopping-cart/" />
ESI 1.0 proposal submitted to W3C 2001
CDN support: Akamai, Fastly
On-premise support: Varnish, Squid, nodesi, etc
<h-include> (client)
<h-include src="/retail/shopping-cart/"></h-include>
Custom Elements version of hinclude.js (by Mark Nottingham, 2006)
Difference: interface and transitive includes
HTTP/2 → Bundles multiple xhr requests into one
Both support refresh()
ESI (server)
+ SEO
+ JS/CSS in responses works as usual
+ Initial performance
+ Cross-domain requests
- Extra infrastructure investment
- Dev perspective (+ i.e. nodesi)
- No streamed responses ⇒ blocking
- Performance sensitive (+ timeouts)
- No header forwarding (+ configuration)
Server transclusion vs client transclusion
h-include (client)
- No SEO
- No loading of JS/CSS in responses
- Delay before xhr requests arrive
- No cross-domain requests (unless CORS)
- If no initial width/height, page will “jump”
+ No extra infrastructure investment
+ No extra dev setup
+ Async ⇒ non-blocking
+ Responses are rendered when they arrive
+ Headers work as usual in browsers
ESI and h-include together (1)
// Refresh-only h-include
var proto = Object.create(HIncludeElement.prototype);
proto.attachedCallback = function() { /* do nothing */ };
document.registerElement('h-include-refresh-only', {
prototype: proto,
});
⇒ <h-include-refresh-only src="...">REFRESHABLE CONTENT HERE</h-include-refresh-only>
h-include-refresh-only
⇒
ESI and h-include together (2)
// Lazy-loading with hunt.js
window.addEventListener('load', function() {
// For these elements...
hunt(document.getElementsByTagName('h-include-refresh-only'), {
// ...when they’re almost in the viewport...
offset: 400,
// ...load them!
in: function() {
this.refresh();
},
});
});
Constraints (need to support)
Continuous delivery
Decentralized governance
Good performance on mobile devices
Rules for integration
Transclude server-side resources
Microservice Websites
Mobile devices
Client-side
dependencies
Shopping cart HTML, check!
But, what about...
Additional client-side behavior? (JavaScript)
Design? (CSS)
How to import service dependencies
Still need cache busting (i.e. /retail/shopping-cart/scripts-aef419.js)
Server-side transclusion works well here
Client-side transclusion require either 1) HTTP redirects, or 2) “loaders” like little-loader for JS and loadCSS
for CSS. Both options are less performant than using server-side transclusion, because of extra client/server
roundtrips.
<head>
...
<!-- Include shopping cart resources (cache-busting-as-a-service) -->
<esi:include src="/retail/shopping-cart/resources/" />
<!-- (scripts go in head too, if defer attribute is used) -->
...
</head>
<body>
...
<-- Include the shopping cart component -->
<esi:include src="/retail/shopping-cart/" />
...
</body>
Load dependencies (before transclusion)
http://caniuse.com/#feat=script-defer, https://www.html5rocks.com/en/tutorials/speed/script-loading/
<head>
...
<!-- Include shopping cart resources (cache-busting-as-a-service) -->
<link rel="stylesheet" src="/retail/shopping-cart/style-ffc864.css" />
<script defer src="/retail/shopping-cart/scripts-aef419.js"></script>
<!-- (scripts go in head too, if defer attribute is used) -->
...
</head>
<body>
...
<-- Include the shopping cart component -->
<div class="shopping-cart">Hello, shopping cart</div>
...
</body>
Load dependencies (after transclusion)
Client-side dependencies: JavaScript
No assumptions of consuming services’ client-side dependencies
⇒ Vanilla JS + polyfills (neither are “free”, so use both with care)
Custom Elements simplifies the lifecycle of components
https://github.com/WebReflection/document-register-element (5 KB)
Client-side dependencies: CSS
Disclaimer: I’m not a CSS expert
Large-scale CSS development is hard (but what’s the alternative?)
Styleguide
Namespacing/components/responsive/flexible
Good news: others have this problem too (i.e. “multi-team SPAs”)
Small global dependency for resets and typography (optional)
Constraints (need to support)
Continuous delivery
Decentralized governance
Good performance on mobile devices
Rules for integration
Transclude server-side resources
No global client-side dependencies
Microservice Websites
Mobile devices
Summary
Services should have their own frontend!
Frontend Frontend FrontendFrontend
Integration: Transclusion
Constraints (need to support)
Continuous delivery
Decentralized governance (otherwise: “occasional” rewrites)
Good performance on mobile devices
Rules for integration (free to use JS frameworks internally, but not for exported fragments)
Transclude server-side resources
No global client-side dependencies
Microservice Websites
Further resources
Microservice Websites, Gustaf Nilsson Kotte
Microservices, James Lewis & Martin Fowler
Self-Contained Systems, Stefan Tilkov et al.
Architecture Without an End State, Michael Nygard
JavaScript Start-up Performance, Addi Osmani
Also, this is a great book:
I’ll happily chat on twitter: @gustaf_nk

Microservice Websites (microXchg 2017)

  • 1.
  • 2.
  • 3.
    How can wedevelop a website with multiple teams? Different business units need to make a website together that should feel like “one website” for the end-user. Example: Retail domain Products, Shopping cart, Checkout, Recommendations, Reviews, User profile, Editorial content, etc... The problem
  • 4.
    Decentralized services butcentralized web? Frontend Bottleneck
  • 5.
    Services should havetheir own frontend! Stefan Tilkov et al, “Self-Contained Systems” Frontend Frontend FrontendFrontend Enables continuous delivery
  • 6.
    Decentralized governance An optionfor teams to choose different tools → heterogeneous/plural/diverse system High rate of change on the frontend Allows the system to evolve over time James Lewis & Martin Fowler, “Microservices: Decentralized Governance”
  • 7.
    Addy Osmani, “JavaScriptStart-up Performance” Mobile device performance
  • 8.
    Constraints (need tosupport) Continuous delivery Decentralized governance Good performance on mobile devices Microservice Websites
  • 9.
  • 10.
    INTEGRATION USE CASE Shoppingcart How can we reuse the shopping cart between different parts of the web site? “Many-to-one” Multiple teams – one shopping cart
  • 11.
    INTEGRATION USE CASE Landingpage The web site’s landing page is the page with the most amount of traffic and many business units want to be represented there. “One-to-many” Multiple teams – one landing page
  • 12.
    Integrating on... What (data/code/content) When(build/run) Where (client/server) Cartesian product → 12 combinations Let’s go through them! Analysis
  • 13.
    What × when× where What? When? Where? Data (APIs) Build Client Server Run Client Server Code (JavaScript, PHP, etc) Build Client Server Run Client Server Content (HTML) Build Client Server Run Client Server What × when × where
  • 14.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client Server Run Client Server Content (HTML) Build Client Server Run Client Server What × when × where
  • 15.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance. Server Run Client Server Content (HTML) Build Client Server Run Client Server What × when × where
  • 16.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance. Server Run Client No diversity + mobile performance. Server N/A Content (HTML) Build Client Server Run Client Server What × when × where
  • 17.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance. Server Run Client No diversity + mobile performance. Server N/A Content (HTML) Build Client Server Run Client Server What × when × where
  • 18.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance Server Run Client No diversity + mobile performance. Server N/A Content (HTML) Build Client N/A Server “Static page fragments” – ok, but limited amount of use cases Run Client Server What × when × where
  • 19.
    What × when× where What? When? Where? Data (APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance. Server Run Client No diversity + mobile performance. Server N/A Content (HTML) Build Client N/A Server “Static page fragments” – ok, but limited amount of use cases Run Client Client-side transclusion Server Server-side transclusion What × when × where
  • 20.
    What? When? Where? Data(APIs) Build Client No reuse at all, only API usage Server Run Client Server Code (JavaScript, PHP, etc) Build Client No continuous delivery. No diversity + mobile performance. Server Run Client No diversity + mobile performance. Server N/A Content (HTML) Build Client N/A Server “Static page fragments” – ok, but limited amount of use cases Run Client Client-side transclusion Server Server-side transclusion What × when × whereWhat × when × where
  • 21.
  • 22.
    Transclusion “transclusion is theinclusion of part or all of an electronic document into one or more other documents by hypertext reference” (Wikipedia: Transclusion) Producer Expose a fragment resource, "/retail/shopping-cart/" Consumer Include the fragment declaratively, similar to <img src="..." /> Transclusion can be used both on server and client Examples: Edge-Side Includes (server) and <h-include> (client)
  • 23.
    No relative URLsin fragments! <img src="images/relative-does-not-work.png" /> <img src="/images/root-relative-often-works.png" /> <img src="https://image-resources.jayway.com/images/absolute-always-works.png" /> ...because URLs are relative to the consumer’s context (not the producer’s)
  • 24.
    Edge Side Includes(server) <-- Include the shopping cart component --> <esi:include src="/retail/shopping-cart/" /> ESI 1.0 proposal submitted to W3C 2001 CDN support: Akamai, Fastly On-premise support: Varnish, Squid, nodesi, etc
  • 25.
    <h-include> (client) <h-include src="/retail/shopping-cart/"></h-include> CustomElements version of hinclude.js (by Mark Nottingham, 2006) Difference: interface and transitive includes HTTP/2 → Bundles multiple xhr requests into one Both support refresh()
  • 26.
    ESI (server) + SEO +JS/CSS in responses works as usual + Initial performance + Cross-domain requests - Extra infrastructure investment - Dev perspective (+ i.e. nodesi) - No streamed responses ⇒ blocking - Performance sensitive (+ timeouts) - No header forwarding (+ configuration) Server transclusion vs client transclusion h-include (client) - No SEO - No loading of JS/CSS in responses - Delay before xhr requests arrive - No cross-domain requests (unless CORS) - If no initial width/height, page will “jump” + No extra infrastructure investment + No extra dev setup + Async ⇒ non-blocking + Responses are rendered when they arrive + Headers work as usual in browsers
  • 27.
    ESI and h-includetogether (1) // Refresh-only h-include var proto = Object.create(HIncludeElement.prototype); proto.attachedCallback = function() { /* do nothing */ }; document.registerElement('h-include-refresh-only', { prototype: proto, }); ⇒ <h-include-refresh-only src="...">REFRESHABLE CONTENT HERE</h-include-refresh-only> h-include-refresh-only ⇒
  • 28.
    ESI and h-includetogether (2) // Lazy-loading with hunt.js window.addEventListener('load', function() { // For these elements... hunt(document.getElementsByTagName('h-include-refresh-only'), { // ...when they’re almost in the viewport... offset: 400, // ...load them! in: function() { this.refresh(); }, }); });
  • 29.
    Constraints (need tosupport) Continuous delivery Decentralized governance Good performance on mobile devices Rules for integration Transclude server-side resources Microservice Websites
  • 30.
  • 31.
    Shopping cart HTML,check! But, what about... Additional client-side behavior? (JavaScript) Design? (CSS)
  • 32.
    How to importservice dependencies Still need cache busting (i.e. /retail/shopping-cart/scripts-aef419.js) Server-side transclusion works well here Client-side transclusion require either 1) HTTP redirects, or 2) “loaders” like little-loader for JS and loadCSS for CSS. Both options are less performant than using server-side transclusion, because of extra client/server roundtrips.
  • 33.
    <head> ... <!-- Include shoppingcart resources (cache-busting-as-a-service) --> <esi:include src="/retail/shopping-cart/resources/" /> <!-- (scripts go in head too, if defer attribute is used) --> ... </head> <body> ... <-- Include the shopping cart component --> <esi:include src="/retail/shopping-cart/" /> ... </body> Load dependencies (before transclusion) http://caniuse.com/#feat=script-defer, https://www.html5rocks.com/en/tutorials/speed/script-loading/
  • 34.
    <head> ... <!-- Include shoppingcart resources (cache-busting-as-a-service) --> <link rel="stylesheet" src="/retail/shopping-cart/style-ffc864.css" /> <script defer src="/retail/shopping-cart/scripts-aef419.js"></script> <!-- (scripts go in head too, if defer attribute is used) --> ... </head> <body> ... <-- Include the shopping cart component --> <div class="shopping-cart">Hello, shopping cart</div> ... </body> Load dependencies (after transclusion)
  • 35.
    Client-side dependencies: JavaScript Noassumptions of consuming services’ client-side dependencies ⇒ Vanilla JS + polyfills (neither are “free”, so use both with care) Custom Elements simplifies the lifecycle of components https://github.com/WebReflection/document-register-element (5 KB)
  • 36.
    Client-side dependencies: CSS Disclaimer:I’m not a CSS expert Large-scale CSS development is hard (but what’s the alternative?) Styleguide Namespacing/components/responsive/flexible Good news: others have this problem too (i.e. “multi-team SPAs”) Small global dependency for resets and typography (optional)
  • 37.
    Constraints (need tosupport) Continuous delivery Decentralized governance Good performance on mobile devices Rules for integration Transclude server-side resources No global client-side dependencies Microservice Websites
  • 38.
  • 39.
    Services should havetheir own frontend! Frontend Frontend FrontendFrontend Integration: Transclusion
  • 40.
    Constraints (need tosupport) Continuous delivery Decentralized governance (otherwise: “occasional” rewrites) Good performance on mobile devices Rules for integration (free to use JS frameworks internally, but not for exported fragments) Transclude server-side resources No global client-side dependencies Microservice Websites
  • 41.
    Further resources Microservice Websites,Gustaf Nilsson Kotte Microservices, James Lewis & Martin Fowler Self-Contained Systems, Stefan Tilkov et al. Architecture Without an End State, Michael Nygard JavaScript Start-up Performance, Addi Osmani Also, this is a great book: I’ll happily chat on twitter: @gustaf_nk