Angular micro-frontend architecture. Part 2/3 — Installing the Nx monorepo and creating micro-frontend apps.
Hello everyone! 👋
In this series of posts I will try to show with a real example how you can use the micro-frontend architecture (hereinafter abbreviated as MFE) in an Angular application. For implementation, we need libraries ngx-mfe, which depends on @angular-architects/module-federation.
If you find an error, please leave a note or leave a comment.
This is post 2 of 3 in the series “Angular micro-frontend architecture”
- The concept of micro-frontend architecture.
- Installing the Nx monorepо and creating micro-frontend apps.
- MFE plugin-based approach. How to load micro-frontend component(s) inside HTML template?
Contents
- Preface
- Create an Nx Workspace
- Add Angular to newly created Nx monorepo
- Create Host and Remote micro-frontend apps
- Overview the Address Form app (Remote)
- Overview the Shell app (Host)
- Adding Angular Material to Apps
- Creating and exposes form component in AddressForm MFE
- Link a Remote app with a Host app
- Conclusion
Preface
It is not necessary to use Nx monorepo, you can also use the standard approach to create a monorepo in Angular with angular.json and projects. But Nx offers a lot more options.More about Nx here.
If you choose not to use Nx, you can create Host and Remote applications using the builder provided in the @angular-architects/module-federation npm package.
To create a Host app (shell) and a Remote app (mfe1) use the following commands in Angular project dir:
ng add @angular-architects/module-federation --project shell --port 5000ng add @angular-architects/module-federation --project mfe1 --port 3000
Create an Nx Workspace
Run the command below to create Nx monorepo:
yarn create nx-workspace WORKSPACE_NAME --packageManager=yarn
Chose an empty workspace
Choose don’t use Nx Cloud
Done
Add Angular to newly created Nx monorepo
After you have successfully created Nx monorepo don’t forget to change to the created directory.
cd WORKSPACE_NAME
To install, you need to run the following command in workspace dir:
yarn add -D @nrwl/angular
Easy! You can now use Nx Generators to generate the Angular apps and libs.
Create Host and Remote micro-frontend apps
We will start with the Shell app which will act as a host application for the MFE:
yarn nx g @nrwl/angular:app shell --mfe --mfeType=host --port=4200 --routing=true
Now, let’s create the AddressForm app as a remote application:
yarn nx g @nrwl/angular:app address-form --mfe --mfeType=remote --port=4201 --routing=true
--mfeType
option specifies the application type — Host or Remote.--port
option specifies the port on which the application will run locally. it will save us from the chance of multiple remote apps trying to run on the same port.
After running this command, you create the two new application in the apps folder address-form and shell.
And in the workspace.json file, we have two applications we created:
Сarefully: if you create a Remote app and don’t import the MFE module, then TS will not be able to find this module at compile time and it will not be included in the built bundle.
The
RemoteEntryModule
generated will be imported inapp.module.ts
file, however, it is not used in theAppModule
itself. This it to allow TS to find the Module during compilation, allowing it to be included in the built bundle.This is required for the Module Federation Plugin to expose the Module correctly.
You can choose to import the MFE module in the
AppModule
if you wish, however, it is not necessary.
These two commands created the following files:
- Created the standard Angular application files.
- Created for each app
webpack.config.js
files, that configured for working withModule Federation
. - Added a
bootstrap.ts
file, moved the code that is normally inmain.ts
tobootstrap.ts
. And changedmain.ts
to dynamically importbootstrap.ts
(this is required for the Module Federation to correct load versions of shared libraries) - Installed Manfred Steyer’s
@angular-architects/module-federation
package (Read more on it here)
Overview the Address Form app (Remote)
Let’s start with the Address Form which would also be called the Remote in terms of Module Federation. This is a micro-frontend app that will be used in the host application.
Let’s see what was generated in the application:
- The auto-generated
RemoteEntryModule
is the module that will be available for loading at run time in the host application.
This is common Angular component which is declared in the RemoteEntry module.
- We also have
AppComponent
andAppModule
as standard Angular app.app.component.ts
andapp.component.html
are almost empty,AppComponent
has<router-outlet>
tag inside template.
As you can see
RemoteEntryModule
, is imported but not used inAppModule
, why it is so described here.
- project.json, this file contains configuration for application like angular.json file. The screenshot below shows the command to launch the AddressForm app and the port on which the application will be launched, in our case it is port 4201.
- And last but not least, this is the webpack.config.js file. We see the following within webpack configuration:
We are only interested in the setting inside the ModuleFederationPlugin
.
Taking a look at each property of the ModuleFederationPlugin
configuration in turn:
name
is the name that Webpack assigns to the Remote app. It usually matches the name of the app.filename
is the name given to the Remote entrypoint that Webpack sets up to allow Host apps to consume the remote application. By convention, this file is usually calledremoteEntry.js
.exposes
is the list of source files that the Remote app provides consuming Host apps for their own use.shared
is a list of libraries that should be shared between the Remote and the Host app. By settingsingleton: true
we ensure that Webpack will only provide one instance of the library across the Host app and the Remote application.
More details about how Module Federation deals with different versions can be found in this article.
In our example we sets of the Remote app name as “address-form”, entrypoint as remoteEntry.js
file, and provide access to our RemoteEntryModule
by the “./Module” key.
We also write shared libraries (all @angular libs and rxjs) that will be shared between Host and Remote application inside builds.
Overview the Shell app (Host)
Shell is a normal Angular app, with the only difference being that it has webpack.config.js:
In webpack.config.js file of Remote app the key difference is we can configure remotes
object. This is where you list the remote applications you want to consume in your host application.
You give it a name that you can reference in your code, in this case address-form
.
Then you assign it a string value of the following pattern: {url}/{remoteEntrypointFilename}
. Example http://localhost:4201/remoteEntry.js
.
url
is the url where the remote application is hosted.remoteEntrypointFilename
is the filename supplied in the remote's webpack configuration.
Now that we have our applications generated, let’s move on to building out some functionality for each.
But you can omit remotes
object and don’t configure it. We don’t define any remotes (micro-frontends) upfront but configure the packages we want to share with the remotes we get informed about at runtime this is Dynamic Module Federation.
- More about how to works Dynamic Module Federation here.
- More about how to works Static Module Federation here.
Personally, I prefer to use Dynamic Module Federation.
Adding Angular Material to Apps
We’ll start by adding Angular Material to our two apps.
yarn add @angular/material
Add theming to root styles.scss
in Shell and AddressForm apps:
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
And finally BrowserAnimationsModule
to app.module.ts
in every apps.
Creating and exposes form component in AddressForm MFE
First create new module:
nx g @nrwl/angular:module form --project=address-form
Then create the address form component with angular material schematics:
nx generate @angular/material:addressForm --name=form --project=address-form --style=scss --changeDetection=OnPush --skipTests
Don’t forget import
FormModule
toapp.component.ts
so that TS can find the given module during compile.
Now let’s configure the ModuleFederationPlugin
in webpack.config.js
Delete the unnecessary module “./Module” from exposes
object and remove folder /src/app/remote-entry
, after add new line with our new FormModule
like that:
We are done with setting up the Remote app, now we can move on to setting up the Host app!
The following Routing steps are optional and are required to configure access to this component when running this micro-frontend as a standalone application:
Configure routing inside FormModule
like this:
And configure routing inside AppModule
like this:
Run AddressForm MFE:
nx run address-form:serve:development
Now our FormComponent
available at htpp://localhost:4201/form
.
Link a Remote app with a Host app
To dynamically load a micro-frontend at runtime, we can use the helper function loadRemoteModule
provided by the @angular-architects/module-federation
plugin:
Let’s run our Shell and AddressFrom apps:
nx serve shellnx serve address-form
Or you can use another command to run all your apps:
nx run-many --target=serve --all=true
If go to htpp://localhost:4200/address-form
, then you will see at the bottom of the page our form component as MFE.
And in the network panel in Chrome you find that file remoteEntry.js
, that will be loaded on demand. If you search in the file, you can find our compiled FormModule
.
Conclusion
As you can see, with this approach, your AddressForm application can be deployed independently and developed independently without forcing you to have to rebuild or redeploy your Shell application. This can lead to a powerful micro-frontend architecture that enables multiple teams to work independently in a single monorepo!
In this tutorial, we exposed a single module that was consumed dynamically as an Angular Route.
If this article was helpful to you, clap 👏 , thanks!
References and Further Reading
- How to create Dynamic Module Federation:
https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/ - How to create Static Module Federation:
https://www.angulararchitects.io/en/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/ - Getting Out of Version-Mismatch-Hell with Module Federation:
https://www.angulararchitects.io/en/aktuelles/getting-out-of-version-mismatch-hell-with-module-federation/