A micro frontend is what?
A frontend application is divided into smaller, independent, modular components that may be built, deployed, and maintained independently in a micro frontend architecture. Since each component (micro frontend) concentrates on a particular area of the application, it is easier to scale and maintain, particularly in complicated and large-scale projects.
This architectural approach divides the user interface into more manageable, autonomous, and independently deployable components in order to construct web applications. Each of these parts—often called micro frontends—represents a particular application feature, page, or capability.
A web application is developed and implemented as a single unit in a classic monolithic design. Conversely, micro frontends disassemble the frontend into smaller components that can be independently developed, tested, and deployed. A distinct release cycle, technology stack, and development team may be assigned to every micro frontend.
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.
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.
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.
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.
- 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
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.
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
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.
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.
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',
},
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)],
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.
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"
}
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'
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));
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.
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
The ng serve command can be used to serve your host app.
Make use of the ng serve command to serve the micro frontend project.
Open the host application. It is localhost:5000 in our instance.
Add a '/' and the name of the micro frontend after it to access the micro frontend. It will be 'localhost:5000/my-first-mfe' for us. The micro frontend's HTML will show up at the bottom of the host application's content.
Conclusion
Even while the initial setup could seem time-consuming, the process is worthwhile in the end.
Among other benefits, the frontend becomes more tested and reusable when it is as modular as feasible.