Architecture Overview
Native Federation is organized into four cooperating layers. Each has a single responsibility and a narrow contract with its neighbors — so you can replace any one of them (your framework, your bundler, or your runtime) without touching the others.
The Four Layers
| Layer | Lives at | Runs | Responsibility |
|---|---|---|---|
| Core | @softarc/native-federation |
build time |
Normalizes the federation config, bundles shared dependencies
and exposed modules, and emits remoteEntry.json +
the import map. Bundler-agnostic.
|
| Adapters | Angular / esbuild / Vite … | build time |
Plug a specific bundler or framework into the Core via the
NFBuildAdapter contract. Often ship a
higher-level API, schematics or CLI integration on top.
|
| Runtime | in the browser | run time |
Reads remoteEntry.json files, constructs a
combined import map, and loads remote modules on demand.
Small, framework-agnostic.
|
| Orchestrator | in the browser | run time |
The next-generation runtime, intended to replace the default
Runtime. Same remoteEntry.json contract, plus
semver-range resolution for shared dependencies and persistent
caching in browser storage. Client-side only — no direct SSR
support yet.
|
How They Fit Together
┌─────────────────────┐ plugs in ┌────────────┐
│ Core │ ◄───────────────── │ Adapter │
│ (federationBuilder) │ │ (esbuild, │
│ │ ──────────────────►│ Angular) │
└──────────┬──────────┘ delegates build └────────────┘
│
│ exposes remoteEntry.json + remotes + shared external bundles
│
▼
┌──────────────────────────────────────┐
│ Runtime ─or─ Orchestrator │
│ (initFederation, loadRemoteModule) │
└──────────────────────────────────────┘
in the browser
Each layer gets its own section elsewhere in these docs. The short version:
-
Core —
bundler-agnostic builder. Normalizes the federation config, computes
the externals your own bundler must leave unresolved, bundles shared
dependencies and exposed modules, and writes
remoteEntry.json+importmap.json. -
Adapters —
framework-/bundler-specific glue implementing the
NFBuildAdaptercontract. First-party adapters exist for Angular and esbuild; there is a community Vite plugin, and you can build your own. -
Runtime — the
small default browser library. Exposes
initFederationandloadRemoteModule. -
Orchestrator
— the recommended browser runtime for v4. Same API as the Runtime,
plus semver-range resolution and persistent
remoteEntry.jsoncaching. Client-side only today; keep the default Runtime for SSR.
Build Steps
At a very high level, building a Native Federation micro frontend goes through five stages. The Core orchestrates; the Adapter does the actual bundling.
-
Init & normalize — the Core loads
federation.config.(m)js, merges it with the project'sFederationOptionsandpackage.json, and registers the Adapter. -
Compute externals — derive the list of shared
packages and
tsconfigpath mappings that the host bundler must leave unresolved, so the browser can later wire them through the import map. -
Bundle shared dependencies — split shared entries
by platform (
browser/node) and bundling strategy (default/package/separate), then hand each group to the Adapter. Results are checksum-cached so unchanged externals are reused on the next build. -
Bundle exposed modules & mapped paths — compile
every
exposesentry plus any monorepo-internal libraries referenced viapaths, again via the Adapter. -
Emit federation artifacts — write
remoteEntry.json(name, shared metadata, exposes, chunks) andimportmap.jsoninto the project'sdistfolder. These two files are the full contract the Runtime / Orchestrator consumes.
A Full Build-to-Runtime Trace
-
Your build pipeline invokes the Native Federation build for
shellandmfe1, each with their ownFederationOptions. -
For each project, the Core reads
federation.config.(m)js, normalizes it, and asks the Adapter to bundle shared externals, mapped paths, and exposed modules. -
The Core writes
dist/<project>/remoteEntry.jsonanddist/<project>/importmap.json. -
The
distfolders are published behind stable URLs. -
At startup, the Runtime (or the
Orchestrator) in
shellfetchesmfe1'sremoteEntry.json, merges shared-dependency metadata, resolves version mismatches, and injects a combined import map into the document. -
When the user navigates to a route backed by a remote,
loadRemoteModuledynamically imports the exposed module — the browser resolves it through the injected import map.
If you only remember one thing: the Core and Runtime speak a simple
contract — remoteEntry.json plus an import map.
Everything else (which bundler, which framework, which runtime) is
swappable.
Where to Go Next
- The Mental Model — why the pieces are shaped this way.
- Terminology — canonical glossary for the terms used above.
- Tutorial — build a working host + remote end-to-end.