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.html which contains any common page elements, and then uses server-side includes to plug in page-specific content from fragment HTML files

  • Build-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:

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 custodianarrow-up-right (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 eventsarrow-up-right 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