@jamarsto/kiunzi-micro-frontend-tools
v0.0.44
Published
Kiunzi framework Micro-frontend scaffolding
Downloads
3
Maintainers
Readme
Kiunzi Micro-frontend Tools
Kiunzi is a scaffolding framework for building microservice based applications. The Kiunzi Micro-frontend Tools library provides support for Module Federation and Custom Elements to enable the development fully encapsulated micro-frontends
🏠Table of Contents
🎁Acknowledgements
This package uses and is inspired by @angular-architects/module-federation
and @angular-architects/module-federation-tools
by Manfred Steyer.
🤔Motivation
There were a few things in the angular architects packages that I felt could be expanded and improved on, and I also thought that more could be done with schematics to get a full implementation up and running without multiple tweaks to the generated code.
The key motivation is to simplify adoption of micro-frontends.
🔍Prerequisites
The general foundations:
- Module Federation requires Webpack 5+
- Angular's support of Webpack 5 requires Angular 12+
- This library is built using Angular 13+
Packages used and automatically added to the package.json
of the angular workspace when Kiunzi is added.
angular-oidc-auth-client
@angular-architects/module-federation
@angular-architects/module-federation-tools
@ng-bootstrap/ng-bootstrap
@popperjs/core
bootstrap
📦Installation
This library is intended to be used at the start of establishing a project as it updates configurations and generates additional code and configurations. In short it builds the scaffolding.
First, build the example application
# Create your angular workspace
ng new micro-frontend-workspace --create-application false
# Navigate into the micro-frontend-workspace folder
cd micro-frontend-workspace
# Create the shared library
ng generate library --project lib
# Create the shell
ng generate application --no-routing --style sass --project shell
# Create the micro-frontends
ng generate application --no-routing --style sass --project mfe1
ng generate application --no-routing --style sass --project mfe2
# Configure the library
ng add @jamarsto/kiunzi-micro-frontend-tools --project lib --type library --authority <your_oidc_server_url> --client <your_client_id>
# Configure the shell
ng add @jamarsto/kiunzi-micro-frontend-tools --project shell --type shell --port 8000 --library lib
# Configure the micro-frontends
ng add @jamarsto/kiunzi-micro-frontend-tools --project mfe1 --type microfrontend --port 8001 --library lib
ng add @jamarsto/kiunzi-micro-frontend-tools --project mfe2 --type microfrontend --port 8002 --library lib
Next, update the tsconfig.json
to add the resolveJsonModule
and esModuleInterop
flags
{
"compileOnSave": false,
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true,
Finally, make sure the library is linked locally so that it is exposed as an npm
package to the build
cd projects/lib
npm link
cd ../..
npm link lib
You may need to restart your ide for the tsconfig.json
and npm link
changes to be picked up
📀Getting Started
Update projects/shell/src/app/app-routing.module.ts
to add the modules to customShellRoutes.moduleRoutes
export const customShellRoutes: CustomShellRoutes = {
headRoutes: [
{ path: '', redirectTo: 'retail', pathMatch: 'full' },
{ path: 'unauthorized', component: UnauthorisedComponent }
],
moduleRoutes: [
{ title: 'Module One', name: 'mfe1', prefix: 'one', items: [], guards: [AutoLoginAllRoutesWithRoleGuard], roles: ['ADMIN', 'USER'] },
{ title: 'Module Two', name: 'mfe2', prefix: 'two', items: [], guards: [AutoLoginAllRoutesWithRoleGuard], roles: ['ADMIN', 'USER'] }
],
tailRoutes: [
{ path: '**', component: NotFoundComponent }
]
}
Update the src/app/remote-app/remote-app-routing.module.ts
for each module to reflect the routing required within the module
const customRoutes: CustomModuleRoutes = {
headRoutes: [
{ path: '', redirectTo: shellModule.prefix, pathMatch: 'full' },
{ path: 'unauthorized', component: UnauthorisedComponent }
],
moduleRoute: { component: RootComponent, guards: [AutoLoginAllRoutesWithRoleGuard], roles: ['ADMIN', 'USER'], children: [
{ path: '', component: HomeComponent },
{ path: 'example', component: ExampleComponent },
]},
tailRoutes: []
}
Update the src/assets/menu.json
for each module to reflect routes we want to add to the shell
navigation bar
{
"menuItems": [
{ "title": "Home", "link": "/", "fullMatch": true },
{ "title": "Example", "link": "/example", "fullMatch": false }
]
}
Add a proxy.conf.json
to the angular workspace, making sure the entries created match the modules created
{
"/mfe/mfe1": {
"target": "http://localhost:8001",
"pathRewrite": {
"^/mfe/mfe1/": "/"
},
"secure": false,
"changeOrigin": true
},
"/mfe/mfe2": {
"target": "http://localhost:8002",
"pathRewrite": {
"^/mfe/mfe2/": "/"
},
"secure": false,
"changeOrigin": true
}
}
Add proxyConfig
to the serve
section of the shell
project in angular.json
"serve": {
"builder": "ngx-build-plus:dev-server",
"configurations": {
"production": {
"browserTarget": "shell:build:production",
"extraWebpackConfig": "projects/shell/webpack.prod.config.js"
},
"development": {
"browserTarget": "shell:build:development",
"proxyConfig": "proxy.conf.json"
}
},
The modules and shell
are now ready to be built and run as normal
✨Development
The shellModule
constant in each src/app/remote-app/remote-app-routing.module.ts
is used to simulate the shell
when testing the micro-frontend in standalone mode
export const shellModule: Module = {name: 'mfe1', title: 'Module One', prefix: 'one', items: jsonMenuItems.menuItems as MenuItems};
The navigation bar in the projects/src/shell/app/component/header/header.component.ts
is dynamically generated using the src/assets/menu.json
from each module
ngOnInit(): void {
this.modules.forEach((entry) => this
.menuItemService
.getMenuItemsForModule(entry.name)
.subscribe((children) => entry.items = children));
}
To keep the routes from the shell
and modules in sync Kiunzi uses events under the hood. To register the event handling we use syncRouteShell.sync
in the constructor
of projects/shell/src/app/app.component.ts
constructor(private router: Router, private syncRouteShell: SyncRouteShell) {
this.syncRouteShell.sync(this.router, customShellRoutes);
}
and we use syncRouteModule.sync
in ngOnInit
of src/app/remote-app/remote-app.component.ts
in each module
ngOnInit(): void {
this.syncRouteModule.sync(this.router, shellModule.name);
}
📄License
This project is licensed under the MIT license. See the LICENSE