Integrating Module Federation with Angular CLI promotes a modular development strategy, enabling dynamic code loading and sharing among various Angular projects. This guide thoroughly explores the necessary steps for this integration, with assistance from the @angular-architects/module-federation
plugin.
@angular-architects/module-federation
plugin installed in your project, as it enhances Angular CLI's capabilities to accommodate Module Federation.To begin utilizing Module Federation, it's imperative to configure the Angular CLI to incorporate Module Federation during the build phase. Due to the CLI's encapsulation of Webpack, the introduction of a custom builder is required to unlock Module Federation's potential.
Provided by the @angular-architects/module-federation
package, this custom builder facilitates the integration process. Start by using the ng add command to incorporate it into your projects:
For those utilizing Nx, the procedure deviates slightly. First, execute an npm install to acquire the library, followed by invoking the init schematic:
The --type
argument, introduced in version 14.3, meticulously ensures that only the requisite configuration is generated.
In our representation, the project labeled 'shell' encapsulates the code for the shell, whereas 'mfe1' denotes Micro Frontend 1. The execution of the aforementioned commands orchestrates several pivotal tasks:
Skeleton Generation:
A rudimentary webpack.config.js
skeleton is crafted to facilitate Module Federation integration.
Custom Builder Installation:
A custom builder is installed, empowering Webpack within the CLI to utilize the generated webpack.config.js
.
Port Assignment:
A distinct port is allocated for the ng serve
command, enabling simultaneous serving of multiple projects.
It's imperative to acknowledge that the generated webpack.config.js
is a partial configuration solely dedicated to governing Module Federation. The remaining configurations are autonomously generated by the CLI, maintaining the usual workflow intact.
This setup lays down a robust foundation for harnessing Module Federation, enabling seamless code sharing and dynamic loading across your Angular CLI projects.
In the realm of Module Federation, the Shell, also referred to as the Host, serves as a pivotal point of integration. This segment delineates the configuration of the Shell to support the lazy-loading of a FlightModule
through routing.
Commence by defining the application routes, specifying a lazy-loaded FlightModule
via a virtual path:
In this configuration, the path 'mfe1/Module'
serves as a virtual representation, indicating that it does not physically exist within the Shell application. Instead, it acts as a reference to a module located in a separate project.
To satisfy the TypeScript compiler, it's necessary to create a type definition for the virtual path:
This declaration guides the TypeScript compiler in understanding the virtual path, easing the import process.
Further, instruct Webpack to resolve all paths prefixed with mfe1
to a remote project. This is achieved within the webpack.config.js
file, generated earlier:
In the remotes
section, the path mfe1
is mapped to the remote micro-frontend, specifically to its remote entry point. This remote entry, generated by Webpack, contains crucial information for interacting with the micro-frontend.
For development purposes, hardcoding the remote entry's URL is sufficient. However, a dynamic approach is necessary for production environments. The concept of dynamic remotes is further explored in a dedicated documentation page on Dynamic Remotes.
shared
property specifies the npm packages to be shared between the Shell and the micro-frontend(s). By using the shareAll
helper method, all dependencies listed in your package.json
are shared. While this facilitates a quick setup, it may lead to an excessive number of shared dependencies, which could be a concern for optimization.singleton: true
and strictVersion: true
settings instructs Webpack to throw a runtime error if there is a version mismatch between the Shell and the micro-frontend(s). Changing strictVersion
to false
would instead result in a runtime warning.requiredVersion: 'auto'
option, provided by the @angular-architects/module-federation
plugin, automatically determines the version from your package.json
, helping to prevent version-related issues.The Micro-frontend, referred to as the Remote in the context of Module Federation, follows a structure similar to a standard Angular application. It includes specific routes within the AppModule
and contains a FlightsModule
for handling flight-related functionalities. This section details the steps to ensure the FlightsModule
is seamlessly loaded into the Shell (Host).
Begin by establishing the basic routes within the AppModule
:
This simple routing setup navigates to a HomeComponent
when the application is accessed.
Proceed to create a FlightsModule
to handle flight-related operations:
This module contains a route to a FlightsSearchComponent
defined as follows:
To enable the loading of FlightsModule
into the Shell, it's imperative to expose it through the Remote's Webpack configuration:
In this configuration:
name
property identifies the micro-frontend as mfe1
.exposes
property signifies the exposure of FlightsModule
under the public name Module
, allowing its consumption by the Shell.shared
property lists the libraries to be shared with the Shell, using the shareAll
method to share all dependencies found in your package.json
. The singleton: true
and strictVersion: true
properties ensure that a single version of shared libraries is used, and a runtime error is triggered in case of version incompatibility, respectively.Having set up the Shell (Host) and Micro-frontend (Remote), it's time to test the configuration to ensure the seamless integration of Module Federation.
Kickstart both the Shell and micro-frontend using the following commands:
Upon executing these commands, the Shell and Micro-frontend will be served, and the respective applications will open in your default web browser.
Navigate to the Flights section in the Shell, and observe the Micro-frontend being dynamically loaded.
The plugin also installs a handy npm script run:all
during the ng-add
and init
schematics, allowing for simultaneous serving of all applications:
For serving selected applications, append their names as command line arguments:
Delving into the main.ts
file, you might notice a slight deviation from the usual:
The standard code typically found in main.ts
has been migrated to a newly created bootstrap.ts
file. This strategic move, orchestrated by the @angular-architects/module-federation
plugin, enhances the Module Federation's ability to make informed decisions about library versions at runtime. The use of asynchronous dynamic imports enables Module Federation to accurately determine and load the correct versions of shared libraries.
While the initial setup with shareAll
offers a straightforward and operational configuration, it may lead to the creation of unnecessarily large shared bundles. For a more refined approach to managing shared dependencies, it's advisable to shift from using shareAll
to employing the share
helper. This allows for more precise control over which dependencies are shared:
In this configuration, the share
helper allows for explicit sharing of selected packages, enabling a more optimized bundle sharing, and a potential reduction in the load times.