At the beginning of this week, I took a course on micro frontends architecture topic. It's such a buzzword when it comes to frontend development, but I've never attempted anything in practice until this week.
Table of contents
- Introduction
- Application architecture
- Prerequisites
- Step-by-step tutorial
- Conclusion
- Additional resources
Introduction
When it comes to microservices, the backend services have this architecture in place for several years already. So why not taking advantage of all the benefits and start developing micro applications / micro components that can be owned, developed, managed and maintained by different teams. Of course, that's only one of the advantages (vertical teams that will handle a feature end-to-end), but we can also talk about the ability to scale the product by working on different features / modules in parallel, speed of delivery, support for innovation and new technologies, more reliable testing and maximum decoupling.
Let's see how it goes in practice!
Application architecture
I've decided to start from the well-known Angular Tour of Heroes tutorial and split the codebase into micro apps. There are a few approaches when it comes to splitting monolithic applications into micro frontends, I chose to do it by feature. The application provides two major features:
- dashboard: top heroes + search heroes capability
- heroes: list of heroes + hero management capabilities (view here, add hero, delete hero)
Considering the split by feature, I've decided on the following architecture of my application:
where:
- shell application is the app that will host all the remote micro applications (dashboard and heroes) and will handle everything related to the navigation (navigation component and routing logic)
- dashboard micro application is one of the remote applications, targeting the dashboard feature
- heroes micro application is the second remote application, targeting the heroes feature
Prerequisites for Angular micro frontends:
- NodeJs 10.13.0+
- Webpack 5
- Angular 12+
- NX
Step-by-step tutorial
🔸 download the Angular Tour of Heroes application
🔸 open terminal to the root directory and run
npm i && npm install -g nx
🔸 transform the Angular cli workspace to an integrated NX monorepository (all the micro frontends will be part of the NX monorepo for simplicity)
ng add @nrwl/angular@<version_number>
🔸 get rid of all the unused files, configs, packages and components to keep it simple and focus on the micro apps (remove the message component and e2e files)
🔸 run the application locally to check that all the changes made so far didn't affect the app
npx nx build tour-of-heroes
So far, you should be able to navigate to http://localhost:4200
and check the application in the browser.
Let's keep having fun:
🔸 generate NX Angular application for dashboard-microapp and hero-microapp using nx cli
npx nx generate @nrwl/angular:app dashboard-microapp
npx nx generate @nrwl/angular:app heroes-microapp
🔸 add angular module federation plugin to all apps (shell, dashboard, hero)
npx nx g @angular-architects/module-federation:init
--project tour-of-heroes --port 4200 --type host
npx nx g @angular-architects/module-federation:init
--project dashboard-microapp --port 4201 --type remote
npx nx g @angular-architects/module-federation:init
--project heroes-microapp --port 4202 --type remote
This command will do a couple of things: add webpack.config.js, webpack.prod.config.js, bootstrap.js files and additional configurations (more details here)
The apps
folder should look like this:
🔸 let's consider the dashboard-microapp (same will apply to hero-microapp). At this point, in src
folder, you have an Angular app configuration - with app.module, app.component.ts, app.component.html and app.component.css. We're going to create a new component, module and routing for the Dashboard feature
ng generate module dashboard --project=dashboard-microapp
🔸 move the logic from the initial application (dashboard and hero-search components) to the dashboard application (you can copy paste the components and update the imports in case the IDE doesn't handle it)
🔸 configure the dashboard.module.ts
as follow (by importing modules, declaring routes and components):
export const remoteRoutes: Route[] = [
{ path: '', component: DashboardComponent },
];
@NgModule({
imports: [
CommonModule,
HttpClientModule,
FormsModule,
RouterModule.forChild(remoteRoutes),
],
declarations: [DashboardComponent, HeroSearchComponent],
})
export class DashboardModule {}
🔸 expose DashboardModule in webpack.config.js
:
const {
withModuleFederationPlugin,
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
name: 'dashboard-microapp',
filename: 'remoteEntry.js',
exposes: {
'./Module':
'./apps/dashboard-microapp/src/app/dashboard/dashboard.module.ts',
},
});
🔸 now that you got the functionality moved to the micro-app and the module configured for this feature, update the app.module.ts:
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
// optional: setting in-memory-web-api for microapps so we can test them separately
HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {
dataEncapsulation: false,
}),
RouterModule.forRoot(
[
{
path: '',
loadChildren: () =>
import('./dashboard/dashboard.module').then(
(m) => m.DashboardModule
),
},
],
{ initialNavigation: 'enabledBlocking' }
),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
To test everything related to dashboard-microapp, let's run it in the browser and see what we get:
npm start dashboard-microapp
navigate to http://localhost:4201 and you should be able to see the dashboard app:
🔸 repeat the same steps for hero-microapp (generate module, move functionality, update hero.module.ts file and app.module.ts, run it in browser and check that everything works as expected).
So far, we have 2 microapps that are running independently in the browser, how do we integrate them into the shell application so we can create the illusion of a single application for the end user?
🔸 switch to shell application (tour-of-heroes) and adjust webpack.config.js:
const {
shareAll,
withModuleFederationPlugin,
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
remotes: {},
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
}),
},
});
keep remotes: {} for dynamic consumption of micro applications
🔸 load the micro apps lazily when it's routed to:
const routes: Routes = [
{
path: '',
loadChildren: () =>
loadRemoteModule({
remoteEntry: 'http://localhost:4201/remoteEntry.js',
type: 'module',
exposedModule: './Module',
}).then((m) => m.DashboardModule),
},
{
path: 'heroes',
loadChildren: () =>
loadRemoteModule({
remoteEntry: 'http://localhost:4202/remoteEntry.js',
type: 'module',
exposedModule: './Module',
}).then((m) => m.HeroesModule),
},
];
🔸 run all the applications
npm run run:all
and navigate to http://localhost:4200. You should be able to see the Tour of heroes application - same as in the beginning, because nothing changed for the end user.
Hoooray! That was it! 🥳
Conclusion
This is my first attempt at micro frontends, I hope it clarifies the initial steps and configuration of the micro frontend architecture and encourages you to consider it for your future projects. I'll keep playing with it and document my stories while learning new stuff, so stay tuned! ^_^
Additional resources:
- Angular Tour of Heroes
- Angular Tour of Heroes code
- Migrate Angular workspace to Nx monorepo
- Angular Module Federation
- Advanced Angular Micro Frontends with Dynamic Module Federation
- Pluralsight Micro Frontends Architecture
- Github repo
Until next time, thank you for reading! 🐾
Top comments (0)