DEV Community

Purneswar Prasad
Purneswar Prasad

Posted on

A story on Frontend Architectures - SPA meets the enterprise (Part-1)

This post might not be useful for everyone. Yes, for starters this is definitely something you can try if you want to know how things work for any small application just to make your hands dirty and gain knowledge.
If you're on a hobby project or a small company, deciding to do this might just be setting yourself up for failure.
But if you're an enterprise with hundreds/thousands of employees working on an application, this might be your best bet at scalability and maintainability.

Welcome to Micro-frontends or MFEs! You're right, it's micro services for the frontend.

And in this post, we'll be discussing 2 things broadly

  • the what and why of MFEs
  • the how with Webpack Module Federation.

Let's start with knowing what a MFE is.

Microfrontend is a self contained micro-app, much like a singular service in micro services architecture of the backend. Each micro-app can be handling a specific type of feature as needed by the organisation, a single business domain or a single feature.
And it will have its own UI, logic, build and deploy pipeline too.

And these micro-FEs need a host which is called shell application. This composes the micro-apps into the final application page.

You may ask, what was the need for MFEs?

As orgs scaled, their backend architecture with micro services was fairly easier to scale than their frontends.
SPA frontends became large monoliths, with a lot of developers contributing to the same enlarged codebase. And this sometimes, blocked parallel workstream teams.

MFEs didn't just make teams leaner and modular, but also brought domain-drive team boundaries into the UI layer.

From a business and technical POV, MFEs can be considered when there is a need for

1) Team autonomy - Separate ownership and pipelines reduces coordination friction
2) Heterogeneous technology with no framework constraint
3) No single-point-of-failure as error in one MFE is less likely to catastrophically affect others. Thus, deliverables can be scaled

A business should NOT use a MFE architecture, if it is a - (i) small app, (ii) small team or (iii) cannot invest in platform engineering/QA/observability.

Now that we've set the premise for MFEs, it's time to discuss the internal working and flow in these micro apps. And for that, we'll be learning a concept from the Webpack bundler called Module Federation.

Before jumping into Module Federation, let's set the ground with a few concepts.

1. Bundlers & the traditional build-time model
Modern frontend apps use bundlers like Webpack and Vite that resolve all imports at build time, producing a single or a few static bundles shipped to the browser.

2. Problem with build-time coupling
In traditional SPAs, every dependency must be known during build time. Which means, a small change requires an entire rebuild and redeploy of the entire FE.

3. Runtime vs Build-time composition
Traditional FEs rely on build-time composition, but MFEs load, connect and execute different applications while the app is running. Thus the need of build-time composition.

4. Need for shared dependencies
In MFE architecture, given the number of different applications, loading multiple copies of large libraries like React is inefficient and dangerous. Thus, these must exist as singletons to avoid runtime bugs.

5. Independent deployment as a primary need
MFEs are not just about code splitting - they're also about enabling teams to work faster, deploy independently and scale at their own pace.
This requires a system that can consume code it didn't know about at build time.

Webpack Module Federation is Webpack’s answer to these problems.

Webpack Module Federation is a runtime feature introduced in Webpack 5, that allows independently built bundles load and
share modules from other apps at runtime. It is one of the most practical technical ways to implement MFEs because it lets those microamps to share code and import each other's modules without rebuilding the whole shell.

Now let's see how the "how" works!

Roles

  • Host(Shell): This is the orchestrator. It consists of global layout, global routing, authentication orchestration and top-level error handling.
    It may also supply global CSS variables and the manifest that maps remote names to URLs.
    It decides when and how to load remotes and provide shared infrastructure like auth token refresh.

  • Remote(MFE): This is an independently built app that owns a business domain/feature. It exposes one or more modules like components via Module Federation's exposes key.
    It has its own CI/CD and publishes a remoteEntry.js file and other static assets.

What is remoteEntry.js

  • This is the bootstrap/manifest file that Webpack emits for a federated(runtime-composed) build. It registers a container(a JS object) which exposes an API to:
    • initialise shared scopes - a place where Webpack coordinates shared dependencies like React, lodash and loads code form each other at runtime.
    • get() factories for exposed modules - A factory function that can create the module that is asked for. A factory is needed because the module might not be loaded yet and the shared dependencies must be resolved first. Thus, for Webpack to control the flow of execution.

(to be continued .....)

Tune in to Part-2 for more internals and deep-dive!

Top comments (0)