Architecture Overview
A bird's-eye view of Native Federation — how the Core, Adapters, Runtime and Orchestrator fit together to enable framework-agnostic Micro Frontends.
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.
- Coming from Module Federation? — the working reference implementation, ported from the webpack Module Federation example.