Webpack - Intro to Federated Modules and Micro-frontends

Webpack - Intro to Federated Modules and Micro-frontends



Information drawn from

What are Micro Frontends?

The term Micro Frontends first came up in ThoughtWorks Technology Radar at the end of 2016. It extends the concepts of micro services to the frontend world.

The current trend is to build a feature-rich and powerful browser application, aka single page app, which sits on top of a micro service architecture. Over time the frontend layer, often developed by a separate team, grows and gets more difficult to maintain. That’s what we call a Frontend Monolith.

frontend monolith

The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface.

frontend monolith

However, this idea is not new. It has a lot in common with the Self-contained Systems concept. In the past approaches like this went by the name of Frontend Integration for Verticalised Systems. But Micro Frontends is clearly a more friendly and less bulky term.

Core Ideas behind Micro Frontends


Be Technology Agnostic

Each team should be able to choose and upgrade their stack without having to coordinate with other teams. Custom Elements are a great way to hide implementation details while providing a neutral interface to others.

Isolate Team Code

Don’t share a runtime, even if all teams use the same framework. Build independent apps that are self contained. Don’t rely on shared state or global variables.

Establish Team Prefixes

Agree on naming conventions where isolation is not possible yet. Namespace CSS, Events, Local Storage and Cookies to avoid collisions and clarify ownership.

Favor Native Browser Features over Custom APIs

Use Browser Events for communication instead of building a global PubSub system. If you really have to build a cross team API, try keeping it as simple as possible.

Build a Resilient Site

Your feature should be useful, even if JavaScript failed or hasn’t executed yet. Use Universal Rendering and Progressive Enhancement to improve perceived performance.

micro-frontend-architecture

The Downside of Micro Frontends

There are several reasons why you might not want to use Micro Frontends.

Module Federation

A scalable solution to sharing code between independent applications has never been convenient, and near impossible at scale. The closest we had was externals or DLLPlugin, forcing centralized dependency on a external file. It was a hassle to share code, the separate applications were not truly standalone and usually, a limited number of dependencies are shared. Moreover, sharing actual feature code or components between separately bundled applications is unfeasible, unproductive, and unprofitable.

We need a scalable solution to sharing node modules and feature/application code. It needs to happen at runtime in order to be adaptive and dynamic. Externals doesn’t do an efficient or flexible job. Import maps do not solve scale problems. I’m not trying to download code and share dependencies alone, I need an orchestration layer that dynamically shares modules at runtime, with fallbacks.

Module Federation is a type of JavaScript architecture Zack Jackson invented and prototyped. Then with the help of co-creator and the founder of Webpack — it was turned into one of the most exciting features in the Webpack 5 core (there’s some cool stuff in there, and the new API is really powerful and clean).

Module Federation allows a JavaScript application to dynamically load code from another application  and  in the process, share dependencies.

If an application consuming a federated module does not have a dependency needed by the federated code,  Webpack will download the missing dependency from that federated build origin.

Code is shared if it can be, but fallbacks exist in each case. Federated code can always load its dependencies but will attempt to use the consumers’ dependencies before downloading more payload. This means less code duplication and dependency sharing just like a monolithic Webpack build.

frontend monolith

Terminology

It’s important to note that this system is designed so that each completely standalone build/app can be in its own repository, deployed independently, and run as its own independent SPA.

These applications are all bi-directional hosts. Any application that’s loaded first, becomes a host.  As you change routes and move through an application, it loads federated modules in the same way you would implement dynamic imports. However if you were to refresh the page, whatever application first starts on that load, becomes a host.

Let’s say each page of a website is deployed and compiled independently. I want this micro-frontend style architecture but do not want page reloads when changing route. I also want to dynamically share code & vendors between them so it’s just as efficient as if it was one large Webpack build, with code splitting.

Landing on the home page app would make the “home” page the “host”. If you browse to an “about” page, the host (home page spa) is actually dynamically importing a module from another independent application (the about page spa). It doesn’t load the main entry point and another entire application: only a few kilobytes of code. If I am on the “about” page and refresh the browser. The “about” page becomes the “host” and browsing back to the home page again would be a case of the about page “host” Fetching a fragment of runtime from a “remote” — the home page. All applications are both remote and host, consumable and consumers of any other federated module in the system.

------------------------------------------------------------------------

Last update on 25 Mar 2022

---