Micro Frontends

From Concept To Practice

On Schedule

  • Why do we need it?
  • What is it?
  • Available technologies
  • Common Approaches
  • Our solution
  • Demo

Front ends are getting bigger

Amplify Dashboard

  • Reports
  • Campaign settings
  • Conversions
  • Segments
  • Payments
  • Video
  • Encapsulation css/js
  • Performance
  • Team Ownership
  • Future Proof
  • Sharing Basics
  • Moduler
  • Communication
  • Seamless UI/UX
  • Easy to Implement

Off The Shelf Solutions?

Partial Solutions

Good old Iframes

Encapsulation css/js

Performance

Team Ownership

Future Proof

Sharing Basics

Moduler

Communication

Seamless UX

Easy to Implement

Build Composition

NPM/MonoRepo

Encapsulation css/js

Performance

Team Ownership

Future Proof

Sharing Basics

Moduler

Communication

Seamless UX

Easy to Implement

Run time Composition

Encapsulation css/js

Performance

Team Ownership

Future Proof

Sharing Basics

Moduler

Communication

Seamless UX

Easy to Implement

Our Solution

Runtime composition

Forgo multi frameworks for now

A little help from Angular...

Dont rely on third party tools

use webpack for encapsulation

Angular Modules

import {NgModule} from '@angular/core'
import {CommonModule} from '@angular/common'

@NgModule({
  imports: [CommonModule],
  declarations: [
    MyComponent,
    MyComponent2
  ],
  providers: [
    MyService
  ],
  exports: [
    MyComponent2
  ]

})

export class MyApp {}

Provide Encapsulation

Can Expose Component Out

Can run as independent app or imported to container app

Have own routing

const routes: Routes = [
  {
    path: 'customers',
    loadChildren: './customers/customers.module#CustomersModule'
  },
  {
    path: 'orders',
    loadChildren: './orders/orders.module#OrdersModule'
  }
import {Compiler, ModuleWithComponentFactories, NgModuleRef} from '@angular/core'

export class ContactsModuleLoader {

  constructor(private compiler: Compiler) {

  }

  loadContacts() {
    import('./contacts/contacts.module').then(this.compileModule.bind(this));
  }

  compileModule(module: NgModuleRef<any>){
    this.compiler.compileModuleAndAllComponentsAsync(module)
      .then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
        console.log('contacts are alive!')
      })
  }
}

Lazy Loading

Dynamic Loading

This only works at build time

Webpack-Share-Loader

var core_1 = __webpack_require__(19);
var ExtComponent = /** @class */ (function () {
    function ExtComponent() {
    }
    ExtComponent = __decorate([
        core_1.Component({})
])
import {Component} from '@angular/core';

@Component({})
 rules: [{
          test: /\.js?$/,
          use: [{
            loader: 'share-loader',
            options: {
              modules: [/@angular/, /@uirouter\/angular/],
              namespace: 'container-app'
            }
          }]
        }]

Share-loader

Container App webpack.config.js

Output:

(function(global) {if(!global["container-app"]) global["container-app"] = {};
if(!global["container-app"]["angular"]) global["container-app"]["angular"] = {};
module.exports = global["container-app"]["angular"]["core"] = 
Object.assign(global["container-app"]["angular"]["core"] || {}, __webpack_require__(231));
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(15)))
 externals: [
    Externals({
      namespace: 'container-app',
      modules: [/@angular/, /@uirouter\/angular/]
    })
  ],
output: {
    library: 'childapp',
    libraryTarget: 'umd'
  },
 "object" == typeof exports && "object" == typeof module 
? module.exports = e(require("@angular/common")) 
: "function" == typeof define && define.amd 
    ? define(["@angular/common"], e) 
    : "object" == typeof exports 
        ? exports.videoApp = e(require("@angular/common")) 
        : t.videoApp = e(t["container-app"].angular.common)

Share-loader

Child App webpack.config.js

Output:

Module

Container app

Reports team

Module

Module

Webpack build

Share-loader

Payments team

Contanier app

Module bundle

Independent deployment

Independent service

Reports team

{   }

Payments team

Container app

At runtime all modules share common resources via a common namespace

Request URL: https://my.outbrain.com/videocampaignscreation/api/external


ResponseBody: 
{
    appName: "videoApp"
    file: "static/external/vidapp.f1a32d630b33f7ffe64e.external.js"
    moduleName: "AppModule"
}

Inter app communication

Container app

App1

App2

App3

Event Bus Instance

Questions?

Copy of Micro Fronends

By Maor Frankel

Copy of Micro Fronends

  • 454