Microfrontend — from tradition to Module Federation !!!

Rohan Ambhore
Globant
Published in
9 min readJul 27, 2020

Microservices has always been a fancy concept to be known and implemented, many organisations have been using this architectural style to avoid limitations of large, monolithic backends.

With this, frontend has also started to follow this footprint by decomposing monoliths frontend into smaller, simpler chunks that can be developed, tested and deployed independently, while still appearing to customers as a single cohesive product. We call this technique micro frontend

“An architectural style where independently deliverable frontend applications are composed into a greater whole”

What are the Benefits of Microfrontend ?

  • Incremental upgrades
  • Decoupled codebases
  • Independent deployment
  • Autonomous teams

In a nutshell, microfrontend is slicing up the big, scary things into smaller and more manageable pieces.

There are various ways to achieve the microfrontend in your projects, we can say this as integration approaches, let explore one by one.

Server-side template composition

Here we render the HTML on server, we have index.html which contain any common page elements and then uses the server side to plug in page-specific content from fragment HTML files.

We serve this file using Nginx, configuration the $PAGE variable by matching against the URL that is being requested:

Here we say it as microfrontend because it has split up the code in such a way that each piece represent a self-contained domain concept that can be delivered by independent team.

ssr microfrontend approch

Build-time integration

The approach where each micro frontend app is published as a package, and have the container app which includes them all as library dependencies. Here is how the package.json will look like

So it produce the single deployable JavaScript bundle, allowing us to de-duplicate common dependencies from various applications, but however this approach makes to re-compile and release every single microfrontend so that to get product release it means we are again lockstep the release process which is the against the guideline of microfrotnend . So its always better to integrate the microfrontend at run-time than at build-time

Run-time integration via iframes

One of the simplest approaches to compose microapps together in a browser is the humble iframe. Iframe makes it easy to build a page out of an independent sub-page, it makes sure that global variables and styling do not interfere with each other.

This iframe based approach is not a new technique and perhaps does not seem to be exciting. This technique comes up with its own cost. It can be difficult to integrate between different parts of the app so they make routing, history and deep-linking more complex and also make extra challenge for making the page fully responsive.

Run-time integration via JavaScript

The other approach which is most flexible and also getting adopted frequently. Here each micro app is included onto the page using a <script> tag and loads the global as its entry-point. The container application then decides which micro frontend to be mounted and calls the relevant function to tell the micro app when and where to get render.

This is the one of the runtime integration approach where we can deploy each of the build file independently. And unlike with iframe we have full flexibility to build integrations between our micro apps, we can also do the lazyload of the bundle.js files and can add some contribution on enhancing the performance.

Run-time integration via Web Components

One of the ways for each micro frontend to define an HTML custom element for the container to instantiate, instead of defining the global function for the container to be called.

This is the way to use the capabilities that the browser provides, could be the good option.

So this was the common approach to integrate the various micro frontends in its own way and at own cost.

Moving forward lets also see one of the main challenges that microfrontend faces i.e, communication.

Cross-application communication

One of the major questions that arises in mind is that How these micro apps talk with each other as we have decoupled them into micro apps but now communication is the main concern.

One of the possible ways is Custom events which allows micro apps to communicate with each other indirectly and a good way to minimize direct coupling but it also makes it harder to determine and enforce the contract that exists between micro frontends.

Second approach is if you are using redux then nothing much greater than that as it is the usual approach to have a single, global and 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.

A third alternative approach is to use the address bar as a communication mechanism.

Basically there are several different approaches to make communication happened within the micro frontend app, so each comes up with up and down sides.

So this was all about the traditional approaches which have been adopted by industries for making micro frontend architectural patterns to happen but now lets see the game changer, How webpack has stepped up to solve the challenges of microfrontend and makes the things much more simple and smoother.

Webpack 5 Module Federation to the rescue !!!

https://giphy.com/

Module federation allows a JavaScript application to dynamically run code from another bundle/build, on client and server.

What is Module Federation?

Very well invented and explained by Zack Jackson, it’s an invented javascript architecture which turned into one of the most exciting features of Webpack 5 core.

It allows a javascript application to dynamically load code from another application, with the outstanding feature as if an application is consuming federated modules and does not have dependency needed by the federated code then webpack will download the missing dependency from that federated build origin. Amazing stuff….

Some Terminology…

Borrowed from webpack-5-module-federation

Module federation: loads the code from another application

A host: a webpack build that is initialized first during a page load (when the onLoad event is triggered)

A remote: another Webpack build, where part of it is being consumed by a “host

Bidirectional-hosts: when a bundle or Webpack build can work as a host or as a remote. Either consuming other applications or being consumed

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.

Module federation has the value adding feature, applications based on Module federation are all bi-directional hosts. So Any application that’s loaded first will becomes a host, it means if you move and change the routes of application then federate module will be get loaded in same way as if you would have implemented dynamic load and in that if you do refresh the page then whatever application first starts on that load, becomes the host. ultimate !!!

So enough with informative text !! now let’s make our hands dirty with code !!

Webpack setup for module federations (bi-directional host).

Image borrowed from https://rangle.io/blog/module-federation-federated-application-architectures/
  • filename: it defines the filename for current application to make it remote (e.g. localhost:3000/remoteEntry.js)
  • exposes: This will be make ‘Widget’ component to be available as a shared component via ‘remoteEntry.js’ and it can then be loaded as ‘import Widget from “application/Widget”
  • remotes: load the applicationHome as remote
  • shared: it add all npm module dependencies as shared modules

Here is the example for Dynamic Remotes — the ability to dynamically load and share code between remotes that are unknown to the host at build time !!

How we are going to approach for this example ?

  1. Webpack setup for host app
  2. Create the useDynamicFederation() hook to lazzy load the sub-app from different remotes
  3. Webpack setup for remote app
  4. Create component in remote app that is going to load in host app remotely

Lets setup webpack for host app, here you will just find the app name and shared react vendor modules.

Now it’s time to define the Dynamic federation hook, this hook will lazy load the remote sub apps which is register to the host app.

Lets see one by one, the approach to integrate the remote sub-app dynamically.

Dynamic script function will take the url of the remote sub-app and inject that url to the script element of existing HTML at runtime so that script will load the remote sub-app dynamically.

Now as sub-app have been loaded, it’s time to initialise the container which may provide the shared module, load component function will do the needful changes for us, which accept the scope and module,

  • scope: the remote sub-app name
  • module: name of the module which is getting exported from the remote sub-app.

With the help of this parameter, load component function will initialize the container with the shared module.

Now let’s put this all together to form the react hook for dynamic federation.

useDynamicRouteMFE() will get system object from the parent and pass this to component <System/> to render the sub app.

Question arise, How to use the hook useDynamicFederation() ?

Here is the snippet for Microfrontend component, this component with the help of useDynamicFederation() render the subApp.

Next we will see how to make remote sub-app ready so that the host app can integrate it dynamically.

This is the Widget component in the sub-app, now we want this to be exported and should be imported in the target host-app with easy configuration.

To makes this happen here our Hero takes entry, that is none other than module federation

In this way we will configure the webpack of the sub-app, with the help of this setting we can make the sub-app remotely available for the host app.

  • File name: Here we can see, we have added file name as remoteEntry.js this file should be injected to the target host app index.html.
  • Exposes: here we export our widget.js component and make it as remotely available.
  • Shared: this will share all the vendor dependencies required for remote sub-app

Demo Time !!!!!

This demo demonstrate the use of module federation for dynamically load and share code between remotes that are unknown to the host at build time.

This is the evolution and game changer for Frontend Architecture, here if you see we don’t need to know the microfrontends during compile time. Instead we could load configuration at runtime which says how many microfronts are available and where they can be found.

For more exciting features just keep a watch on the further development of Module federation.

Coming soon topics …

“Microfrontend — Inter sub-app communications, powered by Module federation”

“Microfrontend — Rock Multi framework with module federation”

“Microfrontend — Module federation for Next.js”

References

--

--