UNPKG

@jamarsto/kiunzi-micro-frontend-tools

Version:
214 lines (171 loc) 7.37 kB
# 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](#acknowledgements) - [Motivation](#motivation) - [Prerequisites](#prerequisites) - [Installation](#installation) - [Getting Started](#getting-started) - [Development](#development) - [License](#license) ## 🎁Acknowledgements This package uses and is inspired by `@angular-architects/module-federation` and `@angular-architects/module-federation-tools` by [Manfred Steyer](https://twitter.com/ManfredSteyer). ## 🤔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 ```sh # 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 ```json { "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 ```sh 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`` ```ts 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 ```ts 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 ```json { "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 ```json { "/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` ```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 ```ts 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 ```ts 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` ```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 ```ts ngOnInit(): void { this.syncRouteModule.sync(this.router, shellModule.name); } ``` ## 📄License This project is licensed under the MIT license. See the [LICENSE](https://github.com/jamarsto/kiunzi-micro-frontend-tools/blob/master/LICENSE)