Hone logo
Hone
Problems

Angular Module Federation: Building a Dynamic Application Architecture

Module Federation is a powerful Webpack feature that enables dynamic code sharing between independently deployable applications. This challenge focuses on implementing Module Federation within an Angular ecosystem to build a scalable and maintainable application architecture. You will learn how to create a host application that loads remote components or modules, fostering reusability and independent development cycles.

Problem Description

Your task is to set up a basic Module Federation architecture for two Angular applications: a "Host" application and a "Remote" application.

Objectives:

  1. Remote Application: Create an Angular application that exposes a specific component (e.g., a simple "Greeting" component) to be consumed by other applications.
  2. Host Application: Create an Angular application that dynamically loads and displays the component exposed by the Remote application.

Key Requirements:

  • Both applications should be standard Angular CLI projects.
  • Webpack 5 (or a compatible version) must be configured to enable Module Federation.
  • The Remote application should define a remoteEntry.js file that exports the module.
  • The Host application should dynamically import the remote module and render the exposed component.
  • The solution should demonstrate seamless communication or data passing between the Host and Remote, if feasible within the scope.

Expected Behavior:

When the Host application is served, it should:

  • Fetch and load the remoteEntry.js file from the Remote application's URL.
  • Instantiate and display the "Greeting" component from the Remote application within its own layout.
  • Any initial data or configuration passed from the Host to the Remote should be reflected in the rendered component.

Edge Cases to Consider:

  • Remote Application Not Available: How does the Host application handle the scenario where the Remote application is offline or its URL is incorrect? (Basic error handling or a fallback is acceptable).
  • Version Mismatches: While this challenge doesn't require explicit version management, be aware of potential issues with shared dependencies.

Examples

Example 1: Basic Component Sharing

Remote Application Setup:

  • remote-app/src/app/greeting/greeting.component.ts:
    import { Component, Input } from '@angular/core';
    
    @Component({
      selector: 'app-greeting',
      template: '<h2>Hello, {{ name }} from Remote!</h2>',
    })
    export class GreetingComponent {
      @Input() name: string = 'Guest';
    }
    
  • Webpack Configuration (webpack.config.js in remote-app):
    • Define remoteEntry.js.
    • Expose GreetingComponent.

Host Application Setup:

  • host-app/src/app/app.component.ts:
    import { Component, OnInit, ViewContainerRef, inject, Type, ApplicationRef, Injector } from '@angular/core';
    // Assume dynamic loading logic is implemented here
    
    @Component({
      selector: 'app-root',
      template: `
        <h1>Host Application</h1>
        <div #remoteComponentContainer></div>
      `,
    })
    export class AppComponent implements OnInit {
      // Inject necessary services for dynamic component loading
      private vcr = inject(ViewContainerRef);
      private appRef = inject(ApplicationRef);
      private injector = inject(Injector);
    
      async ngOnInit() {
        // Dynamically load the remote module and component
        // ... implementation details for dynamic loading ...
      }
    }
    
  • Webpack Configuration (webpack.config.js in host-app):
    • Configure remotes to point to the Remote application's remoteEntry.js.
    • Import the exposed GreetingComponent.

Expected Output (in Host application's browser):

<h1>Host Application</h1>
<div #remoteComponentContainer="">
  <app-greeting><h2>Hello, Guest from Remote!</h2></app-greeting>
</div>

Explanation: The Host application successfully loads and renders the GreetingComponent from the Remote application. The default name input is used.

Example 2: Passing Data to the Remote Component

Host Application Modification:

  • host-app/src/app/app.component.ts:
    // ... inside ngOnInit ...
    const remoteModule = await import('remoteApp/Module'); // 'remoteApp' is the alias defined in webpack
    const GreetingComponent = remoteModule.components.GreetingComponent; // Assuming components are exported like this
    
    const componentRef = this.vcr.createComponent(GreetingComponent);
    componentRef.instance.name = 'World'; // Passing data to the remote component
    this.appRef.attachView(componentRef.hostView);
    

Expected Output (in Host application's browser):

<h1>Host Application</h1>
<div #remoteComponentContainer="">
  <app-greeting><h2>Hello, World from Remote!</h2></app-greeting>
</div>

Explanation: The Host application now passes the string "World" to the name input of the GreetingComponent, and the remote component displays the updated greeting.

Constraints

  • Angular Version: Use Angular v14 or later.
  • Webpack Version: Use Webpack 5.
  • TypeScript: All implementation code must be in TypeScript.
  • Build Process: Both applications must be buildable and serveable independently.
  • File Structure: Organize your project files logically for each application.

Notes

  • Webpack Configuration: The core of this challenge lies in correctly configuring Webpack's Module Federation plugin for both the host and remote applications. Pay close attention to module-federation-plugin options like name, filename, exposes, and remotes.
  • Dynamic Imports: You will need to use dynamic import() statements in the Host application to load the remote modules.
  • Angular Integration: Consider how Angular's compilation and bootstrapping processes interact with Module Federation. You might need to adjust NgModule configurations or explore ways to dynamically create and attach components.
  • Shared Dependencies: For more complex scenarios, managing shared dependencies (e.g., Angular itself, RxJS) is crucial to avoid duplication and ensure compatibility. For this challenge, focus on sharing a custom component.
  • Experimentation: Module Federation can have subtle nuances. Don't be afraid to experiment with different configurations and observe the results.

This challenge is designed to give you hands-on experience with a modern approach to building micro-frontend architectures with Angular, promoting modularity and scalability. Good luck!

Loading editor...
typescript