Micro Frontends
Definition
micro frontends is an architectural style where independently deliverable frontend applications are composed into a greater whole.
In short, micro frontends are all about slicing up big and scary things into smaller, more manageable pieces, and then being explicit about the dependencies between them. Our technology choices, our codebases, our teams, and our release processes should all be able to operate and evolve independently of each other, without excessive coordination.
Pros and Cons
Key benefits:
smaller, more cohesive and maintainable codebases
Independent deployment
more scalable organizations with decoupled, autonomous teams
the ability to upgrade, update or even rewrite parts of the frontend in a more incremental fashion than was previously possible
Potential cons:
can lead to duplication of dependencies
increasing the number of bytes our users must download
dramatic increase in team autonomy can cause fragmentation in the way your teams work
Approaches:
Server-side template composition
We start with a decidedly un-novel approach to frontend development - rendering HTML on the server out of multiple templates or fragments. We have an
index.htmlwhich contains any common page elements, and then uses server-side includes to plug in page-specific content from fragment HTML filesBuild-time integration
One approach that we sometimes see is to publish each micro frontend as a package, and have the container application include them all as library dependencies.
Run-time integration via iframes
One of the simplest approaches to composing applications together in the browser is the humble iframe. By their nature, iframes make it easy to build a page out of independent sub-pages. They also offer a good degree of isolation in terms of styling and global variables not interfering with each other.
Run-time integration via JavaScript Each micro frontend is included onto the page using a
<script>tag, and upon load exposes a global function as its entry-point. The container application then determines which micro frontend should be mounted, and calls the relevant function to tell a micro frontend when and where to render itself.Run-time integration via Web Components
One variation to the previous approach is for each micro frontend to define an HTML custom element for the container to instantiate, instead of defining a global function for the container to call.
Styling
The approach that you pick does not matter all that much, as long as you find a way to ensure that developers can write their styles independently of each other, and have confidence that their code will behave predictably when composed together into a single application.
Approaches:
strict naming convention, such as BEM, to ensure selectors only apply where intended
pre-processor such as SASS, whose selector nesting can be used as a form of namespacing
apply all styles programatically with CSS modules
CSS-in-JS libs, which ensures that styles are directly applied only in the places the developer intends.
shadow DOM also offers style isolation
Shared component libraries
Visual consistency across micro frontends is important, and one approach to this is to develop a library of shared, re-usable UI components. The main benefits of creating such a library are reduced effort through re-use of code, and visual consistency. In addition, your component library can serve as a living styleguide, and it can be a great point of collaboration between developers and designers.
The most obvious candidates for sharing are “dumb” visual primitives such as icons, labels, and buttons. We can also share more complex components which might contain a significant amount of UI logic, such as an auto-completing, drop-down search field. Or a sortable, filterable, paginated table. However, be careful to ensure that your shared components contain only UI logic, and no business or domain logic. When domain logic is put into a shared library it creates a high degree of coupling across applications, and increases the difficulty of change
As with any shared internal library, there are some tricky questions around its ownership and governance. One model is to say that as a shared asset, “everyone” owns it, though in practice this usually means that no one owns it. It can quickly become a hodge-podge of inconsistent code with no clear conventions or technical vision. At the other extreme, if development of the shared library is completely centralized, there will be a big disconnect between the people who create the components and the people who consume them. The best models of ownership and governance are ones where anyone can contribute to the library, but there is a custodian (a person or a team) who is responsible for ensuring the quality, consistency, and validity of those contributions. The job of maintaining the shared library requires strong technical skills, but also the people skills necessary to cultivate collaboration across many teams.
Cross-application communication
Whatever approach we choose, we want our micro frontends to communicate by sending messages or events to each other, and avoid having any shared state. Just like sharing a database across microservices, as soon as we share our data structures and domain models, we create massive amounts of coupling, and it becomes extremely difficult to make changes.
Approaches:
Custom events allow micro frontends to communicate indirectly
React model of passing callbacks and data downwards
Address bar as a communication mechanism
If you are using redux, the usual approach is to have a single, global, shared store for the entire application. However, if each micro frontend is supposed to be its own self-contained application, then it makes sense for each one to have its own redux store.
The most important thing is to think long and hard about what sort of coupling you're introducing, and how you'll maintain that contract over time. Just as with integration between microservices, you won't be able to make breaking changes to your integrations without having a coordinated upgrade process across different applications and teams.
Last updated