← Back to list

Front-End Architecture Using Micro Frontends (MFEs)- Angular 12

Creating large-scale web application is of great challenge. It may have complex codebases or dependencies or duration of release cycles…

Ajaz Abdul Aziz · 2026-04-23 09:44 · 0 claps · 3.1 min read
#angular12 #angular #front-end-development #frontend #module-federation
Open on Medium ↗
Wiki topics: GRW · Growth & Analytics 🌐 · Web Development 🏛️ · Architecture

Front-End Architecture Using Micro Frontends (MFEs)- Angular 12

Creating large-scale web application is of great challenge. It may have complex codebases or dependencies or duration of release cycles coordinating among multiple teams are the most common challenges. MFE offers a solution for this by breaking the single monolithic front end application into smaller independent pieces. So in this article we will explore the concept of MFE, their benefits and how to implement them using module federation.

What is Micro Frontend? Its a standalone module that is capable of handling a functionality of your application. Each module is a small standalone application that can integrate with other modules or micro frontends to create a single unified application experience.

  • Micro frontends can be independently developed, tested and deployed.
  • Micro frontends can focus on a particular functionality or set of functionalities.
  • Each micro frontend can use different framework or technologies if required.

Why use Micro Frontends?

There can be multiple challenges in large application:

  1. Scalability — Multiple teams can work independently on separate modules without interfering with each other.
  2. Faster Deployments — Each Micro Frontend can be deployed independently thereby reducing deployment time and not interrupting existing modules.
  3. Technology Flexibility — Each MFE can use the technology or framework or library (React, Angular, Vue, JavaScript) which is needed for the functionality.
  4. Maintainability — Changes in one MFE will not impact or interfere with rest of the MFE’s or application which thereby reduces updates and removes unnecessary dependency.

What is a Shell Application?

A Shell application (host or container app) connects all the micro frontends. It also handles:

  • Layout & Navigation
  • Dynamic routing
  • Communication between MFE’s
  • Authentication and permissions

How to connect Shell Application with BFE’s?

We use Webpack Module Federation to build MFE Architecture. Module Federation allows to dynamically load host application at run time helping to keep deployments independent and reducing bundle size.

  • Initialization — The MicrofrontendService initializes when the root component of the shell app loads.
  • Load Configuration — Load the routes and configuration whether it be a JSON or URL’s based on local or environments.
  • Dynamic Routes & Authentication — Routes for each MFE are dynamically created. Authentication and permission checks are applied using Angular Route Guards
[
  {
    "name": "user-profile",
    "url": "http://localhost:4201/remoteEntry.js",
    "route": "profile"
  },
  {
    "name": "shopping-cart",
    "url": "http://localhost:4202/remoteEntry.js",
    "route": "cart"
  }
]

Runtime Loading Process

  1. Shell app calls mfeService.initialize().
  2. MFE configurations are loaded.
  3. Dynamic routes are registered with the Angular router.
  4. On navigation, the remote module is lazily loaded.
  5. Authentication and permission checks are performed.
  6. MFE component is rendered inside the shell.
this.router.resetConfig([
  ...this.router.config,
  { path: 'profile', loadChildren: () => loadRemoteModule({ remoteName: 'user-profile', exposedModule: './ProfileModule' }).then(m => m.ProfileModule) }
]);

Dynamic Route Creation

Dynamic routing ensures that new MFEs can be added without modifying the shell app’s static routing configuration:

  • A route helper fetches the remote module bundle.
  • Lazy loading ensures modules are only loaded when needed.
  • Route Guards ensure authentication and permission checks.
  • A default route handles unmatched paths.
function getRemoteModuleForPath(path: string) {
  const config = mfeSettings.find(mfe => mfe.route === path);
  return loadRemoteModule({ remoteName: config.name, exposedModule: './Module' });
}

Module Federation Webpack.config.js file (host)

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;

const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
  path.join(__dirname, 'tsconfig.json'),
  [/* mapped paths to share */]);

module.exports = {
  output: {
    uniqueName: "userProfile",
    publicPath: "auto"
  },
  optimization: {
    runtimeChunk: false
  },
  resolve: {
    alias: {
      ...sharedMappings.getAliases(),
    }
  },
  plugins: [
    new ModuleFederationPlugin({

      // For remotes (please adjust)
      name: "userProfile",
      filename: "remoteEntry.js",
      exposes: {
        UserProfileHandlerModule: './/src/app/user-profile-handler/user-profile-handler.module.ts'
      },

      shared: share({
        "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/forms": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/material": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
        "@ngx-translate/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@ngx-translate/http-loader": { singleton: true, strictVersion: true, requiredVersion: 'auto' },

        ...sharedMappings.getDescriptors()
      })

    }),
    sharedMappings.getPlugin()
  ],
};

Module Federation Webpack.config.js file (shell)

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;

const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
  path.join(__dirname, 'tsconfig.json'),
  [/* mapped paths to share */]);

module.exports = {
  output: {
    uniqueName: "ordersPortal",
    publicPath: "auto"
  },
  optimization: {
    runtimeChunk: false
  },
  resolve: {
    alias: {
      ...sharedMappings.getAliases(),
    }
  },
  plugins: [
    new ModuleFederationPlugin({

      shared: share({
        "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/forms": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@angular/material": { singleton: false, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
        "@ngx-translate/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
        "@ngx-translate/http-loader": { singleton: true, strictVersion: true, requiredVersion: 'auto' },

        ...sharedMappings.getDescriptors()
      })

    }),
    sharedMappings.getPlugin()
  ],
};

메타데이터
post_id
a280fb77cc67
slug
front-end-architecture-using-micro-frontends-mfes-angular-12-a280fb77cc67
url
https://medium.com/@ajasabdulaziz/front-end-architecture-using-micro-frontends-mfes-angular-12-a280fb77cc67
canonical_url
https://medium.com/@ajasabdulaziz/front-end-architecture-using-micro-frontends-mfes-angular-12-a280fb77cc67
author_url
https://medium.com/@ajasabdulaziz
status
ok
fetched_at
2026-06-09 15:37:30