In part 1 of this article, we discussed the micro frontend concept and migration considerations. Now, let's explore implementation strategies. There are two major strategies for modernising frontend applications: the strangler fig pattern and single-page application (SPA) injection.

Frontend migration strategies

The strangler fig pattern introduces a façade, allowing for a gradual shift of routing requests to a new system. This transition continues until the old system is fully replaced, after which it can be decommissioned. The other strategy is SPA injection. With modern SPA development, the SPA itself is responsible for the routing behaviour, making it hard to introduce a strangler façade. This is where SPA injection comes in. The SPA injection strategy involves embedding the new SPA into the HTML document containing the old system and letting it slowly expand in functionality. Depending on your existing architecture, both approaches are great ways to get started with a micro frontend architecture.

Implementing micro frontends isn’t a one-size-fits-all process, as each project has its own context, requirements, and challenges. However, there are some common steps that you can follow to get started:

1. Decide

2. Define

3. Create

Next up is creating the micro frontends. Webpack is a popular compiler for many frontend frameworks. Webpack Module Federation allows multiple separate Webpack builds to dynamically share code or modules with each other at runtime, enabling scalable development across different applications. The added build configuration of a micro frontend project gives it a name and an entry point, the definition of its routes. This configuration produces an extra script file in the build output which is the entry point to the micro frontend application. This extra script file, called the remote entry, is loaded by the application shell at runtime.

Additional configuration can specify which dependencies this bundle wants to share and under which version conditions it is compatible. This makes it possible for the micro frontends and the application shell to share those dependencies, reducing the network overhead. It also allows for sharing data and sharing framework globals as singletons. Finally, this configuration enables the micro frontends to upgrade their dependencies independently when using version compatibility ranges.

4. Orchestrate
The next step is to deploy the micro frontend build output and announce its existence to the discovery service. Common approaches include creating a Docker container and spinning it up in the Kubernetes cluster or distributing the micro frontends to a CDN or Edge Cache. Using manifest files, the frontend resources are described and custom metadata can be added. The Webpack build output along with the metadata is bundled and deployed together. The discovery service retrieves the manifest for all announced bundles and generates a combined manifest. At startup, the application shell asks the discovery service for this combined manifest and resets its router configuration with a route per micro frontend. When a route is navigated to, the necessary micro frontend files are fetched and loaded into the viewport.
Graphic - orchestration micro frontends

5. Integrate

The final step is to integrate the micro frontends with the existing application and provide a smooth migration for the legacy frontend code. There are two options: an outside-in approach (strangler fig strategy) and an inside-out approach (SPA injection strategy). With the outside-in approach, pieces of the legacy frontend are loaded inside the application shell. The application shell takes the role of a strangler façade and must provide all cross-cutting concerns of the legacy system (such as authentication, internationalisation and theming). In parallel, parts of the GUI can be replaced by new micro frontends.

With the inside-out approach, it is possible to embed the application shell in the legacy system, but it must interact with the existing cross-cutting concerns. The advantage of the second approach is that it immediately allows for adding complex user flows without requiring a hefty upfront development effort. One way to accomplish this is by making the application shell available as a web component. With the employment of component attributes, the application shell is instructed programmatically to load the correct micro frontend. After this, the application shell takes over as a regular frontend application.

A benefit of using web components for legacy migration is getting Shadow DOM out of the box. Shadow DOM is part of the web components specification and is implemented in every major browser. Its purpose is to prevent scripting and styling collisions with other parts of the DOM, in this case the legacy frontend.

The challenges and benefits of micro frontends

Micro frontends are not a silver bullet, they come with their own challenges and trade-offs. The first challenge that comes with splitting any system into multiple parts is communication. Micro frontends are intended to decouple individual frontends, but when they communicate, the opposite is achieved. However, using micro frontends in conjunction with an SPA injection migration strategy necessitates communicating with the host system. For asynchronous data needs, you can implement regular REST or GraphQL endpoints. For real-time events, you can adopt an event-driven solution using custom browser events.

Another challenge, specific to migrating backend-generated GUIs to the frontend, is that any session state that was necessary to create a GUI needs to be replicated on the frontend. In simple cases, the state can be moved to the frontend by utilising query params, cookies, HTTP headers, or even using legacy endpoints and migrating the state later. In complex cases, a dedicated service should become responsible.

A different challenge is the user experience. Ideally, there should be a smooth, holistic user experience across all micro frontends. However, due to team autonomy, there’s an increased risk that separate teams develop micro frontends with different UX. The solution to this is the introduction of a design system. This is a set of reusable graphics, GUI code, behaviours and principles. Instead of building every element from scratch, reusable elements are built and shipped as a dedicated set of libraries and patterns. This also speeds up delivery and reduces maintenance effort.

Why should you still consider micro frontends despite these challenges? Because micro frontends also offer benefits that can help overcome some common pain points in frontend development:

  • Team organisation around business domains or features, rather than technical layers or components. This reduces the coordination and dependence between teams, and enables them to work more independently and efficiently, resulting in a faster delivery of features.
  • A gradual modernisation of the legacy frontend code base, without having to rewrite everything. You can start by identifying the most critical or valuable parts of the user interface and replacing them with micro frontends, while keeping the rest of the application intact.
  • Shadow DOM provides a strong isolation to replace pieces of GUI with confidence.
  • The framework and the micro frontends can upgrade independently from each other due to the module federation share configuration.
  • A strong, modular, evolvable frontend can serve as a strangler fig itself to modernise underlying systems.

Micro frontends are a powerful and promising architectural style for frontend development, especially for large and complex applications, and they can help your organisation scale and grow at a fast pace. Now it’s up to you to decide if and how you want to use micro frontends to unleash their power on your organisation.

    Share this article