Micro Frontends for UI
What and Why of Micro-frontend
The Microservice architecture is not a new pattern anymore for the backends. The modern application architecture has embraced this pattern for the cloud native backend platforms. We do not see this principles applied, when it comes to building UI. React-Redux combination is a strong way to separate the view and controller logic on the UI layer, however, there is a growing need for teams to build independent UI apps those can be developed by separate teams and collaborated later to create new experience. Essentially,
UIs are becoming monoliths and we need to extend the microservice ideas to the frontend development.
This article mentions the major steps involved in building micro-frontend systems through a sample app. It also talks about important steps around Webpack and CSS to realize true benefits of this technology.
Sample Application
The sample application supporting this article can be accessed at https://d4e2yqa5hvu3s.cloudfront.net. This is the AWS cloudfront public URL that serves the built project through S3. (No need for any EC2 instance!)
The application is made up of three different standalone UI projects (called micro-frontends) and a host project (container) that serves this micro-frontends. The purpose is to just demonstrate how different front ends can be integrated. I have not implemented the actual backends (dummy data only).
- The home page displays first micro-frontend (Marketing)
- Click on the 'login' on the top right corner to navigate to Auth micro-frontend
- Click on the signin button.(no real userid/pwd required), this takes you to the dashboard micro-frontned.
- You can click on the 'App' on top left corner or Click on 'logout' to go to the home page.
The code for this app is at https://github.com/jaysara/micro-frontends. This github has .github/worklfows directory. This demonstrates how CI/CD is implemented for this project. The CI/CD for this project builds all the micro-frontends projects, deploys them to S3 bucket and invalidates the cloudfrontend cache.
Project Setup & Frontend Integration:
The sample application does not have a real data served by the backend as this article concentrates more on how UI integration for different micro-frontends can be built. Integrating different micro-frontends to collaborate in one application is a critical piece of micro-frontend pattern. There are three main type of known integration patterns when it comes to micro-frontends. There are three known patterns of integrating micro-frontends.
- Build-time Integration : Easy to setup.
- Runtime integration (the sample application uses this)
- Server side- integration:
The Tooling and setup for both server-side and runtime integration are complex. This project uses runtime-integration. In my opinion, runtime integration is the most flexible and performant solution right now. It is little hard to setup as it requires good understanding of the Webpack.
All the projects are setup using simple JS app framework. (no react/angular or Vue framework). All the projects can be run in isolation (on its own). All the projects are integrated through container. This can be realized by running container app.
Module Federation Plugin
ModuleFederationPlugin is important plugin to consider while integrating micro-frontends. This plugin glues the micro-frontend apps with in the main host app.
ModuleFederation Plugin allows developers of the micro-frontend to specify, how a particular micro-frontend can be integrated with other apps.
Here are the steps to do that,
(1) Designate one app as host (in our case Container) and others as Remote (these are marketing, auth and dashboard in my example)
(2) In the remote project, decide which files you want to make available to other projects. (in our case it is -index.js for all the projects.)
(3) Set up "Module Federation Plugin" in the remote projects to expose the files decided in step-2.
(4) Setup "Module Federation Plugin" in the host to fetch the files decided in step-2.
(5) In the host refactor the entry point to load asynchronously
(6) In the host import the files you need from remote.
This architecture allows us to have Zero coupling between the container and child projects. No importing objects, functions, classes etc. No shared state between two applications.
Handling CSS in micro-frontends:
When we build applications separately on our own, we do not anticipate it in the context of other applications. We assume that all the CSS styles will not change during user navigation. This assumption can easily be wrong in the micro-frontends scenario as shown in the diagram below.
How to fix CSS ?
The above problem can be fixed if we generate a unique name for every class in CSS through our javascript. This is called "CSS-IN-JS" option. As shown in the picture below, you can write a javascript with a randomly generated class name. This will generate the css file with the random and (mostly) unique classname.
If you look at the Landing.js, I am using makeStyle function in '@material-ui/core/styles' This generates unique styles to the javascript class. This makes the CSS class unique as well. However this generated class is based on a number (e.g jss1, jss2, jss3 etc). This questions the uniqueness of the generated classname and can cause the collision with other micro-frontends. To avoid this, pass the productionPrefix value specific to the micro-frontend project you are in as shown below.
Verify the generated class names for class for all frontends are unique through devtools
Working with AWS-S3, CloudFront and CI/CD:
This UI is hosted in my S3 bucket and served by a public cloudfront on AWS. This has been a zero cost architecture for me so far. I created Github workflow that builds all the projects and publishes the final build file to S3. There are couple of things that may be important to mention when we build this kind of workflow.
- The access to S3 should be provided to Github for its to upload the file. aws s3 sync command is a good way to push the new files to S3.
- The credentials required to log on to S3 are stored in my Github repository secrets that only I can see/alter. There are different plugins available in Github that allows you to pull these secrets from some kind of vault setup if needed.
The cloudfront does not automatically detect the change of file in the S3 bucket. As a result of new build, your S3 bucket may contain new files, however, cloudfront will still serve the old file from its cache. You must invalidate the cache in cloudfront everytime a new build is built.
You should always evaluate if this pattern is a right fit for you and your application. There are number of ways to build micro-frontends. I have shown one of them. Mostly, your requirements should drive the architecture.
Here are the important takeaways that I have from my exploration of this pattern.
Takeaways :
- Always ask, if you have to change one app in the future, will you have to make changes to the other app. In my example, routing logic is a good example of this. I am using a simple generic interface. If we decide to change the routing library, we do not need to change other parts in the app.
- The Frameworks in UI have been changing more often. Few years from now, everyone may forget React(or something better will come up replacing it). Keeping application stack separate helps you modify one part of UI independent of others.
- Don't forget to scope your CSS. The failure of doing so, can give you a different behavior in your prod than what you see in dev under individual micro-frontend.
I believe Micro-frontend pattern is here to stay and will evolve as more and more javascript frameworks get built. As the demand for full stack developers grows in the industry, this patterns helps developers to have equal design mindset for UI and the backend development. It allows them to apply same principles universally across different tiers of applications. APIs are developed predominantly for the backend/data services. This pattern will allow us to build APIs that serves UI. Let's call it AUI [Application User Interface] for now :-) !!
References :
- https://martinfowler.com/articles/micro-frontends.html
- https://ccbill.com/blog/micro-frontends
- https://hackernoon.com/front-end-microservices-with-web-components-597759313393
Hi, Taking Stephen Grider's Udemy course content (https://www.udemy.com/course/microfrontend-course/?src=sac&kw=microfrontends) and presenting it as your own work without a reference to the creator is stealing. You even used his code and also the diagrams are taken from the course.
Hi sir, this post is very helpful for me! And I have a small question. I have Shell App that does not have any CSS library but in another app (Sub App), I have bootstrap for styling. And when I imported a component from Sub App into Shell App, the class of bootstrap from Sub App didn't work in Shell App. How can I resolve that problem? Thank you for reading this comment, Sir!
In an existing application (classic server-side monolith) how can we attach a microfrontend? Should webcomponents be used to mount the remote application? In your article you had everything in frontend and the container managed it with webpack, if I didn't have this scenario, how could I do it? Thanks a lot and great article
Makes life easy for the support team. Awesome!
Superb article.and very relevant these days