Micro FrontEnd The microservices puzzle extended to frontend
Micro FrontEndThe microservices puzzle extended to frontend
Classic Study Case
An History of Evolution
Front-End
Back-End
Database
Monolith
Front-End
Front & BackFront &
Microservices
Back-End
Database Prod
uct
serv
ice
Sear
ch
Serv
ice
Chec
kout
se
rvic
e
Front-End
It’s Not Only About Architecture
Front-End
Back-End
Database
Monolith
Front-End
Front & BackFront &
Microservices
Back-End
Database Prod
uct
serv
ice
Sear
ch
Serv
ice
Chec
kout
se
rvic
e
Front-End
The Pet Store Team
The Front End Team
The Back End Team
The Front End Team
Prod
uct
Team
Sear
ch
Team
Chec
kout
Te
am
42
1 5
Microservices Benefits
Small & Specialized
Teams
Easier to scale
Micro Services
Freedom in stack’s
choicesEasier to deploy
3
4
Front & Microservices: The perfect match?
Front & Microservices
Prod
uct
serv
ice
Sear
ch
Serv
ice
Chec
kout
se
rvic
e
Front-EndThe Front End Team
Prod
uct
Team
Sear
ch
Team
Chec
kout
Te
am
Next Step of Evolution
Product Team Search Team Checkout Team
Front-End
Back-End
Database
Front-End
Back-End
Database
Front-End
Back-End
Database
Classic E-Commerce Approach
The Petstore Reloaded
Search Team
Product TeamCheckout Team
Customer Focused Teams
Product Team Search Team Checkout Team
Front-End
Back-End
Database
Front-End
Back-End
Database
Front-End
Back-End
Database
Customer Focused Teams
Product Team Search Team Checkout Team
Front-End
Back-End
Database
Front-End
Back-End
Database
Front-End
Back-End
Database
Mission:present the
product in a clear way
Mission:help finding the
right product quickly
Mission:make the checkout
experience easy
3
4
5
2
1
Micro Frontend Benefits
Freedom in stack’s
choicesSmall
Customer Centric Teams
Autonomous development Autonomous
release of features
Application’s evolution made easier
Micro Frontend
Real Life Study Case
July 2015
Manager
March 2017
Governance
August 2017
Operations
: Data Fabric
Real Life Study Case: Data Fabric
V2 is comingTime to reorganize the team!
OCT 2017
December 2017
Layout
March 2018
Galaxy
July 2018
Layout-as-lib
Data Fabric V2
Design System (aka saagie-ui)
Try #1: Saagie-Layout
Try #1: Saagie-Layout
What’s a Web Component?
Custom Elements
Shadow Dom HTML Template
ES Modules
<my-component>
My-component.js Index.htmlclass MyComponent extends HTMLElement {
connectedCallback() { // element has been attached to the DOM }
disconnectedCallback() { // element has been detached from the DOM }
attributeChangedCallback() { // an attribute value has changed }
render() { this.innerHTML = ̀ <div> … </div>`; }}
customElements.define('my-component', MyComponent);
<html lang="en"><head> <title>My application</title>
</head><body>
</body></html>
<script type="module" src="my-component.js">
</script>
<my-component></my-component>
<script src="/lib/document-register-element.js"> </script>
Talking about support...
source
Saagie-Layout
Saagie-Layout
From an Application To a Web Component
class DataManager extends HTMLElement {
constructor() { this.render(); }
render() { this.platform = this.getAttribute('platform'); this.innerHTML =
}}
angular .module('data-manager') .controller('AppController',
AppController);
AppController.$inject = ['$scope', '$rootScope', '$state', … ];
function AppController($scope, $rootScope, $state, ...) {
…}
app-controller.js data-manager.js
customElements.define('saagie-data-manager',DataManager);
`<div ng-app="data-manager" ng-controller="AppController"> <platform-nav platform=${this.platform}> </platform-nav> </div>`;
From an Application To a Web Component
class DataGovernance extends HTMLElement {
connectedCallback() { this.render(); }
render() { this.apiPath = this.getAttribute('datagov-url'); this.innerHTML =
}}
@Component({ selector: 'data-governance’, templateUrl: './app.component.html', providers: []})export class AppComponent {
… }
app-component.js data-governance.js
`<data-governance id="data-governance" datagov-url=${this.apiPath}> </data-governance>`;
customElements.define('saagie-data-governance', DataGovernance);
Saagie-Layout
index.html<html lang="en"> <head>
</head>
<body>
</body></html>
<link rel="stylesheet" href="/saagie-ui/src/index.css">
<script src="/data-governance/web-components/data-governance.js"> </script>
<script src="/data-fabric/web-components/data-manager.js"> </script>
<sla-board></sla-board>
Saagie-Layout
class Board extends HTMLElement {
render(path, user) { const page = this.getPage(path); this.innerHTML =
} …
getPage(path) { let page; switch (path) { case PROJECTS : page = `<saagie-data-manager
id="data-manager" platform=${getSelectedPlatform()}> </saagie-data-manager>`;
break;
case GOVERNANCE : page = `<saagie-data-governance
id="data-governance" datagov-url="${this.dataGovUrl}">
</saagie-data-governance>`;break;
} }}
board.js
`<div class="sui-l-app-layout"> <sla-topbar currentPage="${path}">
</sla-topbar>
<account-menu currentUser="${user}"> </account-menu>
<primary-nav></primary-nav>
<div class="sui-l-app-layout__page"> ${page}
</div> </div>`;
customElements.define('sla-board', Board);
Pros & Cons
ConsPros● consistent user experience ● not a true SPA
● high dependencies on libraries between apps
● bad performances
● routers management● implies to run the entire
stack locally
● possibility to display several components on the same page
● independent deployment
Try #2: Galaxy
Pros & Cons
ConsPros● possibility to share
components between applications
● still high dependencies on libraries between apps
● centralized routing system● but also between React’s
versions
Try #3: Layout-as-Lib
Layout-as-Lib
<html><head> <title>Saagie Governance</title></head><body id="sui-root">
<data-governance-angular class="sui-l-app-layout__subapp"> </data-governance-angular>
</body></html>
index.htmlimport {AppModule} from './app/app.module';
import {LoadLayoutData} from 'saagie-layout-lib';
platformBrowserDynamic().bootstrapModule(AppModule);
main.ts
<saagie-layout current-platform-object='{"loading": true}' hide="true">
</saagie-layout>
const legacy = window .location .pathname.startsWith('/datagovernance');
if (!legacy) { const hrefParameters = window.location.href.split('platform/');
new LoadLayoutData().init('governance', parseInt(hrefParameters[1], 10));
const layout = document.querySelector('saagie-layout'); layout.removeAttribute('hide');}
Layout-as-Lib
import '@webcomponents/custom-elements';
import './web-components/Layout';import './web-components/PrimaryNav';import './web-components/Topbar';
index.jsexport default class LoadLayoutData {
…
LoadLayoutData.js
import LoadLayoutData from './services/LoadLayoutData';
export default { LoadLayoutData}; async loadData(layoutComponent, currentApp,
currentPlatformId) {
await this.loadRights(); await this.loadJobsData(); await this.loadProfile(); }
init(currentApp, currentPlatformId) { const layoutComponent = document.querySelector('saagie-layout');
return this.loadData(layoutComponent, currentApp, currentPlatformId); }}
Layout-as-Lib
import '@webcomponents/custom-elements';
import './web-components/Layout';import './web-components/PrimaryNav';import './web-components/Topbar';
index.jsexport default class LoadLayoutData {
…
LoadLayoutData.js
import LoadLayoutData from './services/LoadLayoutData';
export default { LoadLayoutData};
async loadRights(){ const rights = await fetch(apiRightsUrl); this.login = rights.login; this.setUser({ login: this.login }); this.setPlatforms(rights.platforms)); }
setPlatforms(platforms) { this.layoutComponent .setAttribute('platforms-list-object', JSON.stringify({platforms})); }
Layout-as-Lib
layout.jsclass Layout extends HTMLElement { …
initRender() { this.innerHTML = `<div class="sui-l-app-layout" id="sui-root">
<saagie-topbar class="sui-o-topbar"> </saagie-topbar> <saagie-primary-nav class="sui-o-primary-nav"> </saagie-primary-nav> ${this.innerHTML}
</div>`; }}
connectedCallback() { this.initRender(); … }
attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'current-app': this.changeChildAttribute ('saagie-topbar', name, newValue); this.changeChildAttribute ('saagie-primary-nav', name, newValue); break;
case 'current-platform-object': …
if(!customElements.get('saagie-layout')){ customElements.define('saagie-layout', Layout);}
Pros & Cons
ConsPros● not an SPA at all● no components
aggregation possible
Page Transitions
Page Transitions
Page Transitions
Page Transitions
Pros & Cons
ConsPros● way more faster● easier to integrate
● breaking changes implies to update each application
● possible inconsistent user experience
● independent deployment AND development
● no more libraries conflict● can be run on its own
Data Fabric
July 2015
Manager
March 2017
Governance
August 2017
Operations
Data Fabric V2
V2 is comingTime to reorganize the team!
OCT 2017
December 2017
Layout
March 2018
Galaxy
May 2018
Security
Data Fabric V2
September 2018
Project & Jobs
July 2018
App StoreDecember 2018
Dataset Access
June 2018
Layout-as-lib
Best practices, tips & tricks
Think usecase first Take care of your design
Share ressources, not runtime!
Aggregation of components or aggregation of SPA?
Design system, optimistic UI, skeletons ...
Build independent and self contained apps
Share libraries and common services
● Micro-frontends.orghttps://github.com/neuland/micro-frontendsMicro Frontends talk by Michael Geers at Web Rebels, Oslo 2018 @nataltis
● Experiences Using Micro FrontEnds at IKEAhttps://www.infoq.com/news/2018/08/experiences-micro-frontends
● Project Mosaic (Zalando)https://www.mosaic9.org/
● Single SPAhttps://single-spa.js.org/
To go further
Thank you!