Pandora E-Commerce · Architecture

Build Tools Never Touch Production

① Webpack & Turbopack compile
② Output: static files
③ Only files deploy
① Build Machine / CI
Developer laptop · CI server · runs before deployment
Turbopack
Compiles the full Next.js app — pages, SSR, Server Components, App Router, HMR
next dev -p 3021 / next build
⚡ Blazing fast · Instant HMR · Rust-based
Next.js 16 Default · Officially Recommended
📦Webpack Runner
Standalone watcher — only job is compiling Module Federation exposure into one file
node scripts/federation.js
⏱ Slower · Parallel process · CSR output only
Next.js Fully Supported · Opt-in alongside Turbopack
⛔ Both tools stay on this machine Never installed on the production server
② Compiled Output
Static artifacts — the only things that move to production
~2.4 MB
📁.next/
Compiled Next.js app · HTML · JS chunks · CSS · SSR functions
Just compiled
manifest
📄remoteEntry.js
Module Federation manifest · exposes shared components
🌐 Pure Vanilla JS — no framework, no runtime. Any browser reads it natively.
Just compiled
Like compiled CSS — no Sass on the server.
Files deploy. Tools stay behind.
Deploy Gate
📁Files
✓ Deploys
vs
Tools
📦
✗ Blocked
LIVE
③ Production Server
Cloud · CDN · No bundlers installed
Node.js Runtime
JavaScript engine on your server
Makes JavaScript run on the server — same language as the browser, but in the cloud. Handles every visitor's HTTP request.
Receives browser HTTP requests
Runs SSR from .next/
Serves remoteEntry.js as static file
Does NOT run Webpack or Turbopack
No bundlers installed here
.next/
Node.js serves SSR pages & static assets
remoteEntry.js
Static HTTP · browser loads & executes directly
Not installed on server
Turbopack — build tool only
📦Webpack — build tool only
1 / 3
Pandora E-Commerce · Architecture

Risk Assessment

🛡
Every Concern Has a Proven Answer
Architecture decisions were made to eliminate risk before it exists — not react to it later
Turbopack has no native Module Federation support
A standalone Webpack process runs separately — Turbopack never touches it. Zero conflict. Two processes, zero shared state.
✓ Resolved by design
Webpack is slower than Turbopack
Webpack only builds the federation manifest — designed to handle chunks and scale with performance. Runs in parallel, never blocks Next.js, never slows the developer. Turbopack stays fast.
✓ No dev impact
🖥
No SSR for remote components across apps
Each app SSRs its own routes natively in Next.js. Remote components load CSR. Team tests SSR first, then confirms runtime sharing works.
✓ Tested & confirmed
🔮
Future: Turbopack adds native MF support
One config file already shared. When Vercel ships native MF, just delete the Webpack runner. No rewrite. Migration takes low effort.
✓ Future-proof
🧪
Proven in POC
feature/web-runtime-api · live & working
🛒 Shared cart state across apps 📦 Shared component library 🔗 Cross-app navigation — no reload 🧩 Runtime module sharing (remoteEntry.js)
Live & Verified
Trade-offs — Accepted & Understood
🟡
Two parallel processes per app — each optimised for its role
⚡ Turbopack handles fast dev & HMR · 📦 Federation Publisher handles MF exposure. Separate concerns, zero interference.
CSR loading for federated components is intentional
Each app owns its SSR via React Server Components. Cross-app components hydrate on the client — a deliberate boundary, not a limitation.
🟡
Singleton versioning requires care
Inherent to Module Federation in general — not specific to this pattern. Managed through shared dependency versioning in federation.config.js.
🛡
Worst-case mitigation — Feature flag on remoteEntry.js
If anything goes wrong with the federation manifest, a feature flag can instantly disable remote loading and fall back to a full page reload. Zero downtime, customers unaffected — flip the flag, fix, re-enable.
None Low Med High
LOW ✓
Production Risk
Webpack & Turbopack are build-time tools — same category as TypeScript. They compile and step away.
2 / 3
Pandora E-Commerce · Architecture

React Shared Scope — How It Actually Works

HAPPY PATH · YOUR SETUP FALLBACK · STANDALONE ONLY Browser loads host chunks React in memory · Turbopack build 0azr8x.ig8u4o.js · 71.5 kB SYNC createInstance() runs SYNC lib:()=>React → share scope populated Executes before any network request fires RemoteLoader mounts → loadRemote() remoteEntry.js fetch begins · t=39ms Always fetched — carries shared candidates React in shared scope? YES ★ your setup NO scope empty init() finds React ✓ Host's React already in scope get():i.e(86) → skipped entirely PLPPage renders r(67) = host's React object Same in-memory reference · zero cost ✓ Zero extra download · 86.js absent get():i.e(86) called Scope empty — fetching chunk Async CDN request initiated 86.js downloads from CDN ~449 kB · extra network round-trip ⚠ Two React instances possible ⚠ Fallback active · standalone mode @module-federation/enhanced share scope · version negotiation · singleton enforcement createInstance() sync execution ensures scope is populated before loadRemote() fires any network request REAL SOURCE · Host bundle // 0ju4wz-gbxwdx.js · 25 kB shared.react = { lib: () => React, // ← pointer to loaded module // no get(), no network request lib: = "I already have it" REAL SOURCE · remoteEntry.js // 24.7 kB · always fetched react: [{ get: () => i.e(86) .then(() => () => i(86)), eager: false, // lazy — if needed singleton: true get: = "I can provide it IF needed" BROWSER DEVTOOLS · Network Tab · VERIFIED 86.js (React chunk) ──────────── ABSENT remoteEntry.js ── ✓ 24.7 kB loaded . WHEN DOES THIS HAPPEN? Remote served standalone (no host MF init) Or: createInstance() was not called before load
3 / 3