Day 27 — Micro-Frontends: Scalable Architecture with Module Federation
📘 Day 27 — Micro-Frontends: Scalable Architecture with Module Federation
Zero to Hero — Hands-on Angular Tutorial
Today you will learn:
- ✔️ What are Micro-Frontends?
- ✔️ Module Federation (Webpack 5)
- ✔️ The Shell vs. Remote concept
- ✔️ Setting up a Host App (Shell) and a Child App (Remote)
- ✔️ Loading a Remote Micro-Frontend at runtime
Micro-Frontends allow you to split a massive app (Monolith) into smaller, independent apps that can be deployed separately but run together in the browser. 🤯
🟦 1. Why Micro-Frontends?
Imagine Amazon.com.
- Header Team: Deploys the search bar.
- Product Team: Deploys the item details.
- Cart Team: Deploys the checkout.
If the Cart Team breaks their code, the Search Bar should still work. If the Product Team wants to upgrade to Angular v20, they shouldn’t force the Header Team to upgrade instantly.
Micro-Frontends enable:
- Independent Deployments.
- Independent Tech Stacks (Angular Shell loading React Child).
- Team Autonomy.
🟩 2. Module Federation (The Magic)
We use @angular-architects/module-federation to handle the complexity.
Scenario:
- Shell: The main app (Menu, Layout).
- MFE1: A “Profile” app (Remote).
Step 1: Create Projects (Nx or CLI)
# If using standard CLI
ng new shell
ng new mfe1
Step 2: Add Module Federation
ng add @angular-architects/module-federation --project shell --port 4200 --type host
ng add @angular-architects/module-federation --project mfe1 --port 4201 --type remote
This creates a webpack.config.js in both apps.
🟧 3. Configure the Remote (Child)
We want to expose a standardized Module or Component from mfe1 so shell can use it.
mfe1/webpack.config.js:
module.exports = withModuleFederationPlugin({
name: 'mfe1',
exposes: {
// We expose THIS component to the world
'./Component': './src/app/app.component.ts',
// Or expose a whole Module with Routes
'./Module': './src/app/profile/profile.module.ts'
},
shared: {
...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' })
},
});
exposes: The public API of this Micro-Frontend.shared: Helps both apps share the same Angular instance (prevents loading Angular twice).
🟥 4. Configure the Shell (Parent)
The Shell needs to know where mfe1 lives.
shell/webpack.config.js:
module.exports = withModuleFederationPlugin({
remotes: {
// "mfe1" is the name defined in the remote config
// "http://localhost:4201/remoteEntry.js" is the entry file generated by webpack
"mfe1": "http://localhost:4201/remoteEntry.js",
},
shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }) },
});
🟫 5. Loading the Remote Route
Now, in the Shell’s router, we lazy load the Remote just like a normal module!
shell/src/app/app.routes.ts:
import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/module-federation';
export const routes: Routes = [
{
path: 'profile',
loadChildren: () =>
loadRemoteModule({
type: 'manifest',
remoteName: 'mfe1',
exposedModule: './Module',
}).then((m) => m.ProfileModule),
},
// Or load a standalone component
{
path: 'widget',
loadComponent: () =>
loadRemoteModule({
type: 'module',
remoteEntry: 'http://localhost:4201/remoteEntry.js',
exposedModule: './Component',
}).then((m) => m.AppComponent),
}
];
🟦 6. Running it
- Start MFE1:
ng serve mfe1(Ports 4201). - Start Shell:
ng serve shell(Ports 4200). - Open
http://localhost:4200. - Navigate to
/profile.
Result: The Shell (4200) downloads the code from MFE1 (4201) on the fly and renders it. 🚀
🎉 End of Day 27 — What You Learned
Today you broke the monolith:
- ✔️ Module Federation: The standard for sharing JS code at runtime.
- ✔️ Host vs Remote: Organizing the architecture.
- ✔️ Exopes/Remotes Config: Wiring up Webpack.
- ✔️ Runtime Loading:
loadRemoteModulein the Router.
🧪 Day 27 Challenge
“Micro-Dashboard”
- Create
Shellapp (4200). - Create
Statsapp (4201). - In
Stats, create a standaloneStatsCardComponent. - Expose the component in
Statswebpack config. - Load the component in the
Shellhomepage using*ngComponentOutlet(or Routing). - Verify that if you stop the
Statsserver, the Shell still works (but that part fails gracefully).