Setting up Micro Frontend with latest version on Angular


Developer collaborating. Image here.

What is a micro frontend?

A micro frontend architecture involves breaking down a frontend application into smaller, independent, and modular pieces that can be developed, deployed, and maintained separately. Each piece (micro frontend) focuses on a specific part of the application, making it easier to manage and scale, especially in large and complex projects.

This architectural style for building web applications by decomposing the user interface into smaller, self-contained, and independently deployable components. Each of these components, often referred to as micro frontends, represents a specific feature, page, or functionality within the application.

In a traditional monolithic architecture, an entire web application is built and deployed as a single unit. Micro frontends, on the other hand, break down the frontend into smaller parts that can be developed, tested, and deployed independently. Each micro frontend can have its own development team, technology stack, and release cycle.

What is 'module federation'?

Module Federation is an architectural pattern in web development that brings a revolutionary approach to building frontend applications. It allows developers to break down large monolithic frontend applications into smaller, independently deployable, and loosely coupled modules, often referred to as micro frontends.

Module Federation addresses several challenges in this context:

  • Decentralized Development
    • Challenge: Micro frontends need to be developed independently by different teams.
    • Solution: Module Federation allows each micro frontend to have its own codebase, dependencies, and build process, enabling decentralized development.
  • Dynamic Module Loading
    • Challenge: Loading all modules upfront can lead to slow initial page loads.
    • Solution: Module Federation supports dynamic module loading, where modules are loaded on-demand as users interact with the application. This optimizes the initial loading time.
  • Loose Coupling
    • Challenge: Micro frontends should communicate without tightly coupling their codebases.
    • Solution: Module Federation ensures loose coupling by defining clear module boundaries and communication interfaces. Micro frontends can interact via APIs or messaging without direct dependencies on internal details.
  • Versioning and Compatibility
    • Challenge: Coordinating versions of shared dependencies is complex.
    • Solution: Module Federation provides a mechanism for sharing and managing dependencies. Each micro frontend specifies its dependencies, and the build system ensures compatibility.
  • Seamless Integration
    • Challenge: Micro frontends must seamlessly integrate when necessary.
    • Solution: Module Federation allows for shared components or functionality, ensuring that micro frontends can work together seamlessly when needed.
  • Optimized Code Splitting
    • Challenge: Efficiently splitting code into smaller, manageable chunks is crucial for performance.
    • Solution: Module Federation optimizes code splitting by intelligently sharing common code among micro frontends while preserving their autonomy.

In technical terms, Module Federation facilitates a modular, dynamic, and loosely coupled architecture, providing a scalable solution for large and complex web applications built with a micro frontend approach.

Getting started

Structuring a system that operates on a micro frontend architecture can be broken down into 2 main parts.

  1. The host application
    This is also referred to as 'main app' or 'shell app'. It is the application that will consume the micro frontend. You can consider it as the application that the user will use and for it to be fully working, it needs to use the micro services.
  2. The microservice
    Micro frontends are independent, self-contained applications or modules that contribute to the overall user interface.Each micro frontend is developed and maintained by a separate team, allowing for decentralized development. Micro frontends can be features, sections, or components of the larger application. They communicate with the host app and other micro frontends using defined APIs or messaging mechanisms. Micro frontends can be developed using different technologies, frameworks, or even languages, providing flexibility to development teams.

Creating the host app

  1. Generate a new angular project called host app using the command below
    ng new host-app --standalone false
    Angular will ask for your preferred style sheet format and if you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering). For the latter, I selected "No". The answer to these two questions are up to you.
  2. Once your project is generated, move into the host app. Below is the command in the terminal to do so:
    cd host-app

    Then, run the following command to all module federation in your host app:

    ng add @angular-architects/module-federation

    You will be asked if you want to proceed. Obviously, say "Yes" !!
    You will also be asked your project name. You can enter the same name as your angular project, that is, "host-app".
    You will have to choose on which port to run the project. I chose "5000".
    Once the command is completed, the host-app is 'micro frontend' ready.

NOTE

You will notice that 2 new files have been created.

The first one is webpack.config.ts and the second one is webpack.prod.config.ts.

The webpack.config.ts and webpack.prod.config.ts files are configuration files for Webpack, a popular JavaScript module bundler. These files define how Webpack should process, bundle, and output your project's assets, such as JavaScript files, stylesheets, and images.

The file contains the configuration for development (or sometimes referred to as the "dev" configuration). The development configuration is optimized for speed and provides additional tools for debugging.

The second one contains the same configuration but for production.

Creating a micro frontend

  1. Generate a new angular project called host app using the command below
    ng new mfe-one --standalone false

    Angular will ask for your preferred style sheet format and if you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering). Here again, I selected 'No'. The answers to these two questions will depend on you.

  2. Once your project is generated, move into the host app

    cd mfe-one

    Then, run the following command to all module federation in your host app:

    ng add @angular-architects/module-federation

    You will be asked the same questions as for the host app. Answer the same way except for the port. All ports to be used should be unique and different be it the host app and all the micro frontends. For our case, we will use port 7000.

  3. In webpack.config.js, you will see a comment 'For remotes'.
    It is under this that you will configure how the micro frontend will be exposed.
    Following this comment, the next 4-5 commented lines are an example already given to you when you install module federation.
    To get started, uncomment the lines starting with: 'name', 'filename' and 'exposes'.
    The property 'name' can stay the same.
    The property 'filename' must remain 'remoteEntry.js'.
    The property 'exposes' should contain an array of a list of how to expose the micro frontend to the host application.
    For our micro frontend, we do the following:

    // For remotes (please adjust)
           name: "mfeOne",
           filename: "remoteEntry.js",
           exposes: {
               './my-first-mfe': './/src/app/app.module.ts',
          }, 
    
  4. In the app-routing.module.ts file, modify the routes constant to the following:
    const routes: Routes = [ 
     {
       path: '',
       component: AppComponent,
       pathMatch: 'full',
     }
    ];
    
    Also, change the imports list to the following:
    imports: [RouterModule.forChild(routes)],
  5. In the app.component.ts, update the 'selector name' to the following
    selector: 'app-root:not(p)'

    Also, change the name of the export class from 'AppComponent' to something more unique. We have changed ours to 'MyFirstMfeAppComponent'. This will result in making some updates in the app.module.ts, where the App Component is imported and needs to be changed to the new name.

    NOTE: The first part of the key value pair in the 'exposes' array is user defined. You get to choose it, but its value needs to be a module. It can be the module file for a component or the app module component like in our case.

These steps are all the steps required to create a micro frontend. Now we need to connect the micro frontend and the host app.

Connecting the micro frontend and the host app

All of the following steps will be done in the host app.

  1. In the assets folder, create a file and call it 'mf.manifest.json'. This file will contain an array of all the micro frontends that the host app will consume. Remember to add all your micro frontend here. Below is ours:
    {
       "my-first-mfe": "http://localhost:3000/remoteEntry.js"
    }
    
  2. In the src folder, create a 'decl.d.ts' file.
    In this file, declare the path of the module of the micro frontend.
    The path is in the form <name of the micro frontend project>/<name in the 'exposes' part of the webpack config in the micro frontend project>
    Ours will look like below:
    declare 'mfe-one/my-first-mfe'
  3. Modify the main.ts file to add the following code:
    import { loadManifest } from '@angular-architects/module-federation';
    
    
    import('./bootstrap')
       .catch(err => console.error(err));
    
    
    loadManifest("/assets/mf.manifest.json")
     .catch(err => console.error(err))
     .then(_ => import('./bootstrap'))
     .catch(err => console.error(err));
  4. In the app-routing.module.ts, add the path for the micro frontend in the 'routes' list. Do not forget to add the following import:
    import { loadRemoteModule } from '@angular-architects/module-federation';

    The 'routes' list should look like below:

    const routes: Routes = [
     {
       path: 'my-first-mfe',
       loadChildren: () =>
         loadRemoteModule(
           {
           type: 'module',
           remoteEntry: 'http://localhost:7000/remoteEntry.js',
           exposedModule: './my-first-mfe',
         }
         ).then((m) => m.AppModule),
     }
    ];
    

    The 'path' is the url path to access the page from the micro frontend. For example here, http://localhost:5000/my-first-mfe. The value of the path is up to you.
    The 'remoteEntry' is the path of the remoteEntry.js file for your micro frontend. Typically, it is the url of your micro frontend,i.e. Localhost:<<port you chose when add module federation in your micro frontend>>/remoteEntry.js.

    The 'exposedModule' is the one defined in the webpack.config.js of your micro frontend.

  5. In the webpack.config.js file, uncomment the lines after the comment (For hosts (please adjust)). In 'remotes', we need to add the url to the remoteEntry.js files for the micro frontend. Below is how to do it for our example:

    remotes: {
               "mfeOne": "http://localhost:7000/remoteEntry.js",
    
           },
    

    Note that port 7000 is used here as it is the port we defined when adding module federation in our micro frontend.

Running the project

  1. Serve your host app using the ng serve command.
  2. Serve the micro frontend project using the ng serve command.
  3. Access the host app. In our case, it is localhost:5000

To access the micro frontend, add a '/' followed by the name of the micro frontend. For us, it is going to be 'localhost:5000/my-first-mfe'. You will see the html of the micro frontend appear at the bottom of the content of the host app.

Conclusion

Despite the initial setup which may appear long, the return is worth the time invested in this process.

Having the frontend as modular as possible, makes it more testable and more re usable among others.

References

My repo