Discover the power of microfrontends: A revolution in frontend development
In recent years, a new architectural concept has emerged in front-end architecture: microfrontends.
This approach is a radical change in the way we think about a front-end application. In recent years, we have been used to thinking of front-end applications as large, single monoliths; with this new concept, we will break these monoliths into smaller, independently deployable modules.
Each module — or microfrontend — encapsulates a specific feature of the application, allowing teams to work autonomously and deliver quickly without impacting other parts of the system.
But… does this sound familiar?
If the first thing that comes to mind is “microservices”, you’ve got it. 🎉
The essence of microfrontends is to apply the principles of microservices architecture. Just as microservices promote back-end modularity and autonomy, microfrontends extend this functionality to the front-end, enabling teams to develop, test, and deploy components independently.
Ispect microfrontend principles
So, what are the principles of a microfrontend architecture?
Contracts
Working with contracts is essential in the development of microfrontend architectures. Individual components should hide their implementation details, exposing only the minimum necessary to interact with each other.
This practice allows teams to focus on the component without worrying about the work of other teams, and to be sure that the work of one team does not interfere with the work of another one.
Independency
With monoliths, we have to deploy the entire system every time we make a small change. This has a high risk of failure and much longer deployment and rollback times. A microfrontend architecture overcomes this problem by allowing individual components to be deployed independently.
Teams can focus on their work without having to wait for dependencies on external features or worry about what other teams are doing.
Automation
If you have not already done, I recommend reading this other article
Here I explained how automation is critical to software development and delivery. Concepts such as continuous integration and continuous delivery/deployment are now fundamental in modern times.
The ability to break the front-end into smaller, self-contained modules facilitates the automation of deploying these independent units across environments. This is also a key concept of microservices architecture. A strong culture of automation is essential to move faster and more reliably.
Isolate Failures
In microfrontend architectures, the user-interface is usually composed at runtime. This can lead to network errors or even 404s if resources are not available. Techniques for displaying different content or hiding a specific part of the application can be used to improve the user experience.
With this approach, a failure in one component will not affect the entire application.
Where to start with microfrontend architectures ?
In order to build a microfrontend architecture, it is important to understand how to apply it. There are two application styles:
Vertical Split
The vertical split provides the closest developer experience to a single-page application.
In the vertical split, we have an application shell that takes care of mounting and dismounting the microfrontends. In this case the relationship is 1:1, the shell only loads one microfrontend at a time.
Typically, in these cases, the application routing is split into two parts, a global one that is used to load the individual microfrontend, and an internal one that is used to navigate between the internal pages of the microfrontend.
If it is necessary to communicate across different views, the use of query parameters is recommended for volatile data, and web storage/cookie is recommended for persistent data such as tokens or user local settings.
Horizontal Split
In the horizontal split, we always have one application shell that takes care of loading multiple microfrontends. In this case, the relationship is 1:N.
This structure is more difficult than the previous one because it is necessary to determine how microfrontends should communicate with each other when they are in the same or different views.
When using horizontal split, you must avoid sharing any state between microfrontends. In horizontal split, the microfrontend should communicate through event emitters, custom events or reactive streams using a publish/subscribe pattern. This helps to maintain the decoupling and independence of all of them.
How can I implement a microfrontend architecture?
To implement a microfrontend architecture we have three different choices:
- Iframes
- WebComponents
- Module Federation
Iframes
Iframes may not be the first choice that comes to mind for a microfrontend solution, but they provide incredible isolation.
An iframe is an inline frame used inside a webpage to load another HTML document in it. It can communicate with the host page using the postMessage method. This allows the micro-frontend to notify any user interaction with the shell through events, and the application can then trigger other activities (share the event with other iframes, change part of the user interface in the host, etc.).
Currently, the implementation of microfrontends with iframes is mostly done on desktop applications. It is not recommended for web applications because iframes require high CPU consumption, which has a negative impact on performance.
Web Components
Web Components are a set of web platform APIs that allow you to create custom encapsulated HTML tags for reuse. Web components allow us to encapsulate custom styles without fear of them leaking into the main application.
Nowadays there are a lot of frameworks and libraries that help to build web components and they’re one of the most used solutions for building microfrontends.
Web components consist of three main technologies:
- Custom elements: They are an extension of HTML components. We can use them as containers of our microfrontends.
- Shadow DOM: A set of JavaScript APIs for attaching an encapsulated “shadow” DOM tree to an element, rendered separately from the main DOM. This allows us to style components without fear of collision with other parts of the document.
- HTML templates: The template of the component. It’s the basis of the custom element’s structure.
Module Federation
With Webpack 5, a new feature has helped microfrontends proliferate: Module Federation.
Module Federation allows JavaScript code to be loaded — synchronously or asynchronously — at runtime.
There are two components in Module Federation:
- Host: It’s the container. It can contain one or more microfrontends and is responsible for loading libraries.
- Remote: It represents the single microfrontend or library to be loaded into the host at runtime. The remote exposes one or more objects that can be used by the host.
One of the strengths of this feature is the ability to share external libraries between different remotes without worrying about runtime conflicts. In fact, you can specify which libraries to share, and Module Federation will take care of loading a single version for all microfrontends that use the library, optimizing the bundle size of the entire application.
(Stay tuned, there will be a practical example on module federation 🤖)
Microfrontends are the way?
If you have reached this point and are wondering, “Are microfrontends the solution?” well… maybe the answer is **NO**.
As with any architectural choice, each project must evaluate its use on a case-by-case basis; not all cases can be addressed with microfrontends. (Just as not all use cases are suitable for microservices).
This architecture provides a solution to the major limitations of monoliths. It allows you to break it down into smaller, independent components. It is more scalable. It allows faster development and deployment of individual components. It is technology agnostic and allows you to vertically structure teams around each functionality, from back-end to front-end, without external disruption.
On the other hand, there are many challenges to overcome. It is a complex architecture that requires careful analysis to determine whether a vertical or horizontal split is more appropriate. It requires guidelines to be shared with teams to decide how components will communicate with each other, and it also requires an eye on bundle size and performance (the ability to use different frameworks could increase bundle size and traffic).
So, do your analysis and always weigh up the pros and cons of each solution ✨.
Thank you for reading this article!
Any questions or suggestions? Feel free to write a comment.