Build Multi-Repo Micro-Frontend Using Angular And Webpack 5 Module Federation

Angular is one of the popular Single Page Application framework. It is commonly used to build monolithic front-end applications. It supports the lazy loading of modules, allowing the browser to load the feature modules on demand. Angular internally uses Webpack as the bundling tool. Micro-frontend is a pattern that allows you to build the frontend applications as individual applications(remote) that can be integrated within a shell(host) application. Module federation plugin of the Webpack allows you to load these micro-frontend applications into a shell application.

This tutorial helps you to build Micro-frontends using Angular and Webpack Module Federation.

Prerequisites

  1. Angular CLI: Version 14.0.0 (use exact version)
  2. Module Federation library for Angular (@angular-architects/module-federation): 14.3.0 (compatible to Angular 14.0.0)
  3. Node 16.x or later
  4. Visual Studio Code

Build first remote(Micro-frontend) application (Auth-MFE)

1) Open the command terminal and create an Angular workspace for the first micro-frontend application. Run the following command to create a workspace for auth-app-remote .

ng new auth-app-remote --create-application false --skip-tests

2) Switch to the workspace folder and create a project inside the auth-app-remote.

cd auth-app-remote
ng g app auth-mfe --skip-tests --routing

3) Add a home component to the application.

ng g c components/home --project auth-mfe

4) Create a feature module for the auth-mfe project.

ng g module --project auth-mfe --routing auth

5) Add a component login to the auth feature module.

ng g c --project auth-mfe --module auth auth/components/login

6) Updates the auth-routing.module.ts file with the following route configuration.

const routes: Routes = [{
    path: 'login',
    component: LoginComponent
}];
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class AuthRoutingModule {}

7) Update the app-routing.module.ts file with the following routing configuration.

const routes: Routes = [{
    path: '',
    component: HomeComponent,
    pathMatch: 'full'
}, {
    path: 'auth',
    loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule)
}];
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

8) Install the bootstrap and jquery libraries to the project.

npm install [email protected] jquery --save

9) Open the app.component.html file and replace the existing code with the following code.

<nav class="navbar navbar-expand-lg bg-primary bg-body-tertiary" data-bs-theme="dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Auth MFE</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" href="#">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="auth/login">Login</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

10) Run and test the application.

ng serve --port 4200 -o

Configure the application with module federation

11) Install the Module federation plugin to the application. Install the supported version of the @angular-architects/module-federation based on the Angular CLI. In this demo, we are using Angular 14.0.0, so the supported version of the module federation plugin is 14.3.0.

ng add @angular-architects/[email protected] --type remote --project auth-mfe --port 4200

12) Open the webpack.config.js file, and update the name and exposes properties.

const {
    shareAll,
    withModuleFederationPlugin
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
    name: 'auth-mfe',
    exposes: {
        './Module': './projects/auth-mfe/src/app/auth/auth.module.ts',
    },
    shared: {
        ...shareAll({
            singleton: true,
            strictVersion: true,
            requiredVersion: 'auto'
        }),
    },
});

13) Run and test the application.

ng serve --port 4200 -o

Build second remote(Micro-frontend) application (Book-MFE)

1) Open the command terminal and create an Angular workspace for another micro-frontend application. Run the following command to create a workspace for book-app-remote .

ng new book-app-remote --create-application false --skip-tests

2) Switch to the workspace folder and create a project inside the book-app-remote.

cd book-app-remote
ng g app book-mfe --skip-tests --routing

3) Add a home component to the application.

ng g c components/home --project book-mfe

4) Create a feature module for the book-mfe project.

ng g module --project book-mfe --routing book

5) Add a component book-list to the book feature module.

ng g c --project book-mfe --module book book/components/book-list

6) Updates the book-routing.module.ts file with the following route configuration.

const routes: Routes = [{
    path: 'list',
    component: BookListComponent
}];
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class BookRoutingModule {}

7) Update the app-routing.module.ts file with the following routing configuration.

const routes: Routes = [{
    path: '',
    component: HomeComponent,
    pathMatch: 'full'
}, {
    path: 'books',
    loadChildren: () => import('./book/book.module').then(m => m.BookModule)
}];
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

8) Install the bootstrap and jquery libraries to the project.

npm install [email protected] jquery --save

9) Open the app.component.html file and replace the existing code with the following code.

<nav class="navbar navbar-expand-lg bg-primary bg-body-tertiary" data-bs-theme="dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Book MFE</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" href="#">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="books/list">Books</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

10) Run and test the application.

ng serve --port 4200 -o

Configure the application with module federation

11) Install the Module federation plugin to the application.

ng add @angular-architects/[email protected] --type remote --project book-mfe --port 4300

12) Open the webpack.config.js file, and update the name and exposes properties.

const {
    shareAll,
    withModuleFederationPlugin
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
    name: 'book-mfe',
    exposes: {
        './Module': './projects/book-mfe/src/app/book/book.module.ts',
    },
    shared: {
        ...shareAll({
            singleton: true,
            strictVersion: true,
            requiredVersion: 'auto'
        }),
    },
});

13) Run and test the application.

ng serve --port 4300 -o

Create a Shell(Host) Application (BookStore-Host)

1) Open the command terminal and create an Angular workspace for a shell application. Run the following command to create a workspace for bookstore-host .

ng new bookstore-host --create-application false --skip-tests

2) Switch to the workspace folder and create a project inside the bookstore-host.

cd bookstore-host
ng g app bookstore-shell --skip-tests --routing

3) Add a home component to the application.

ng g c components/home --project bookstore-shell

4) Update the app-routing.module.ts file with the following routing configuration.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './components/home/home.component';

const routes: Routes = [{
    path: '',
    component: HomeComponent,
    pathMatch: 'full'
}];
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

5) Install the bootstrap and jquery libraries to the project.

npm install [email protected] jquery --save

6) Open the app.component.html file and replace the existing code with the following code.

<nav class="navbar navbar-expand-lg bg-primary bg-body-tertiary" data-bs-theme="dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Bookstore</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" routerLink="">Home</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

7) Run and test the application.

ng serve --port 5000 -o

Configure the shell application with module federation

8) Install the Module federation plugin to the application.

ng add @angular-architects/[email protected] --type host --project bookstore-shell --port 5000

9) Open the webpack.config.js file, and update the remote property.

const {
    shareAll,
    withModuleFederationPlugin
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
    remotes: {
        "auth-mfe": "http://localhost:4200/remoteEntry.js",
        "book-mfe": "http://localhost:4300/remoteEntry.js"
    },
    shared: {
        ...shareAll({
            singleton: true,
            strictVersion: true,
            requiredVersion: 'auto'
        }),
    },
});

10) Open the app-routing.module.ts file and update the routes for loading the auth and book micro-frontends.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './components/home/home.component';

const routes: Routes = [{
    path: '',
    component: HomeComponent,
    pathMatch: 'full'
}, {
    path: 'auth',
    loadChildren: () => import('auth-mfe/Module').then(m => m.AuthModule)
}, {
    path: 'books',
    loadChildren: () => import('book-mfe/Module').then(m => m.BookModule)
}];
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

11) Update the app.component.html to add the hyperlinks for invoking book list and login components from the micro-frontends.

<nav class="navbar navbar-expand-lg bg-primary bg-body-tertiary" data-bs-theme="dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Bookstore</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" routerLink="">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="books/list">Books</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="auth/login">Login</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

12) Run and test the application.

ng serve --port 5000 -o