What Made Us Switch to Micro Frontend?
The micro frontend architecture pattern can be understood as a decomposition style that aims to bring the decoupled nature of microservices to frontend applications. Just like microservices, the micro frontend architecture encourages building frontends as various smaller and self-contained deployments. In this article, we explore why micro frontends work, and how we approached the implementation recently.
Why Micro Frontends Work
Understanding Architecture Quantum
In physics, quantum is defined as a minimum amount of any physical entity involved in an interaction. This also applies to the software architecture world. An architectural quantum is an independently deployable component with high functional cohesion. Architects use architectural quantum to “understand” the amount of coupling in distributed architectures. Monolithic applications have an architectural quanta of 1, since it is an entire deployment unit. On the other hand, distributed applications tend to have a bigger quanta.
Traditional “API Driven” services architecture
Usually, people tend to break their back-ends into various microservices that are independent from each other in terms of functional cohesion. It can be seen in the diagram below:
Note that each microservice is an independently deployable component. This approach yields an architecture quantum of 3 (which is high enough for our teeny tiny example). Usually, architects try to achieve a bigger number in distributed systems to ensure loose coupling.
A low enough architecture quantum indicates tight coupling, which also brings in issues like increased testing scope with each deployment. And that is just scratching the surface. The age-old mantra of “the bigger the better” surprisingly applies to this aspect of software architecture.
Factoring in the Frontend
Now that the back-end is ready, it’s time to build the software component that makes up our presentation layer, i.e, frontends. Traditionally, developers build their front-end applications to tie up all backend functionality. Which ends up looking like this:
Seems about right? And yet, the devil is in the details.
Let’s say that a team that owns a certain backend has decided to update some critical fields in their APIs. The frontend team needs to accommodate for those changes and make a deployment. And since there’s just one frontend application, the deployed unit might contain potentially incomplete integration routines written for backends that could be in a totally different development phase.
This also increases the testing scope drastically even for an application that is an entirely different deployment unit! Not to mention jumping through hoops like cherry-picking specific commits into temporary branches and then creating deployments out of them. It’s pretty evident that our architectural quantum has been reduced to just one.
Enter Micro Frontends
Summing up so far, we would like our frontend applications to have these characteristics:
- Independently deployable (and testable)
- Composable into smaller components
- Independently maintainable by different teams
Independently Deployable Components
As microservices go, different teams might be in charge of different frontend implementations, so it is desirable for us to have the “independence” factor present at the VCS level itself, i.e. A micro frontend application should have its own codebase.
Having different codebases allows us to run CD processes independent of other frontend components. Smaller codebases will also allow increased testability of components, and have smaller “blast radiuses” if things were to go South.
Keeping frontend applications separate also enables development of frontends in a technology agnostic manner. Some micro frontend solutions like Webpack’s Module Federation plugin gives us sufficient tooling to pull off amazing things just like this. For instance, one team can develop their frontend applications using React, another team might do it using Vue, and both of these applications can work together and use a shared state!
Usually, all deployed micro frontend applications serve their resources off their own domains. Though in some applications, it makes more sense to have them serve their traffic on an internal address, and having a gateway server (like Nginx) to proxy traffic to them.
At Clowre, we opted to go with the latter approach, since we wanted to tie everything together behind a single domain. And since we have various fast moving development teams working on various projects, we built a custom proxy with Go that discovers and ties all micro frontend applications together. We at Clowre use highly customized build scripts in conjunction with Module Federation to package our applications.
Tying It All Together
So far we have made it clear that each micro frontend app can perform its work in isolation, and in theory it works! But in practice, it takes just a bit more than that. We’ll often run into scenarios in which frontend components will need some configuration passed down to them. This configuration can also be context-bound, .i.e., changing based on where these components are being consumed from. This is usually done by having a “main” app that can load the entry points of various frontend applications in the web browser on demand. This main app essentially behaves as a “gateway server”, and has no business logic built into it. It is important to code the main app in such a way that it doesn’t create any static dependency on other frontend apps, as it can reduce the architectural quanta and we will be back on square one!
Pitfalls To Lookout For
As everything in software architecture, some risks are always there which can cause major issues if not taken care of. If you are considering micro frontends, keep in mind that:
- Managing styles can be a tricky part when various frontend applications are sharing a browser window. Since any CSS loaded by the browser affects ALL DOM elements, it is quite easy to mess up styling by loading conflicting instructions through multiple apps.
- When working with the “main app pattern”, we need to be extra careful with including dependencies. A small change in the “shared” code might bring in breaking changes.
- Libraries that create contexts over the whole DOM (like Redux, Material, react-router-dom, i18nxt) must have the exact versions in all frontend apps to avoid major breaking issues.
- Apps consuming a remote frontend must keep track of any major changes regarding base configuration.
This blog has presented the advantages of micro Frontends and how they can be beneficial while developing a modern web app that is lightweight, easy to test, and scalable. Clowre by Sarvika Technologies is a specialized platform that has built numerous state-of-the-art solutions for its clients by making robust applications. Through Clowre, we have resolved business challenges of our clients and let them achieve a superior outcome. Get in touch with us for discussing your next project or to share any feedback by writing to us at firstname.lastname@example.org.
Thanks for reading, here’s some Led Zeppelin for your enjoyment 🙂
Written by Dushyant Sharma
If there is someone in Sarvika who has humongous stories of adventures to tell, it's Dushyant. An avid traveler who finds his joy in riding his bike and exploring new places in mountains every year. A MBA finance, bachelor in electronics and communication and a full stack developer, he is working as a Technical Lead of our frontend team. As per Dushyant, one should always stay updated about the work they do and where the industry is heading next. You should consider his opinion if you are planning to travel somewhere in the Himalayas.