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:
- Remote Application: Create an Angular application that exposes a specific component (e.g., a simple "Greeting" component) to be consumed by other applications.
- 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.jsfile 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.jsfile 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.jsinremote-app):- Define
remoteEntry.js. - Expose
GreetingComponent.
- Define
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.jsinhost-app):- Configure
remotesto point to the Remote application'sremoteEntry.js. - Import the exposed
GreetingComponent.
- Configure
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-pluginoptions likename,filename,exposes, andremotes. - 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
NgModuleconfigurations 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!