Back to list
dev_to 2026年3月14日

マイクロフロントエンド、モノリシック vs MFE: ホスティング戦略、横断的な通信、モジュール連合

Micro Frontends, Monolith vs MFE

Translated: 2026/3/14 12:00:18

Japanese Translation

マイクロフロントエンド、モノリシック vs MFE、ホスティング戦略、クロス通信およびモジュール連合について深く解説します。単一の UI シェルの下で複数の MFE がどのように構成されるか、MFE 間の通信パターン、そして Webpack モジュール連合の完全ガイド(コードサンプル付き)を解説します。 **モノリシックフロントエンドアーキテクチャ** **マイクロフロントエンドとは?** モノリシック vs マイクロフロントエンドのトレードオフ MFE インテグレーション / 構成のアプローチ 単一の UI 下での複数の MFE ホステリング MFE 間のクロス通信 モジュール連合深層解析 モジュール連合 2.0 以降 共有依存関係とバージョン管理 マイクロフロントエンドにおけるルーティング MFE のデプロイと CI/CD リアルワールドの事例 意思決定フローチャート:どれを選ぶべきか モノリシックフロントエンドとは、すべてのページ、機能、ルーティングが単一の統合されたコードベースで存在するものです。1 つのチーム(または同じリポジトリで作業する複数のチーム)が、アプリケーション全体を単一のユニットとしてビルド、テスト、デプロイします。 ┌──────────────────────────────────────────────────┐ │ Monolithic SPA │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Header │ │ Sidebar │ │ Footer │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Auth │ │ Product │ │ Cart │ │ │ │ │ │ │ Module │ │ Catalog │ │ Module │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Search │ │ Profile │ │ Orders │ │ │ │ │ │ │ Module │ │ Module │ │ Module │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ Single Build → Single Bundle → Single Deploy └──────────────────────────────────────────────────┘ **詳細観測:コードベース** 単一のリポジトリ、単一のビルドパイプライン **詳細観測:デプロイメント** すべての変更に対してオールまたはナッシングのデプロイ **詳細観測:チーム** 高い結合性——すべてのチームが同じコードに触れます **詳細観測:バンドルサイズ** 機能が増えることで線形に増加します(コード分割で緩和されます) **詳細観測:一貫性** 自然——共有設計システム、共有状態 **詳細観測:オンボーディング** アプリが大きくなると難しくなります;1 つの巨大なコードベースを学習する必要があります ┌────────────────────────────────────────────────────────────────────┐ │ Monolith Build & Deploy Flow │ │ │ │ │ │ Developer A (Auth) ──┐ │ │ Developer B (Cart) ──┼──▶ Single Repo ──▶ CI Pipeline │ │ Developer C (Search)──┘ (merge to main) │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ │ Lint + Test │ │ │ │ │ (ALL code) │ │ │ │ └──────┬──────┘ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ │ Build │ │ │ │ │ (Entire App)│ │ │ │ └──────┬──────┘ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ │ Deploy │ │ │ │ │ (All or │ │ │ │ │ Nothing) │ │ │ │ └─────────────┘ │ │ └────────────────────────────────────────────────────────────────────┘ 重要な観察:Developer A の 1 ラインの認証修正が、Developer B の未完成のカート機能がマージまたは取り消されるまで待たなければならないという事実。 面接の知見:面接者は「なぜモノリシックのままにならずに?」とよく質問します。真実は、モノリシックは悪くありません。ほとんどのアプリケーションにとって、正しい初期点はモノリシックです。問題は、アプリケーションのスケールではなく、組織的なスケールから発生します。優れたモジュール境界、遅延ルーティング、およびコード分割がある構造化されたモノリシックは、数年間製品のサービスを提供できます。マイクロフロントエンドへの移行の決定は、主に組織的なアーキテクチャ上の決定であり、技術的な決定ではありません。 中小型アプリで 1–3 つのチーム 機能は密接に結合されています(例えば、すべてのウィジェットが状態を共有するダッシュボード) 組織には、独立したデプロイサイクルの必要性はありません 反復速度が最も重要である初期段階の製品 なぜモノリシックが実際に正しいデフォルトなのか: モノリシックは、座標化オーバーヘッドゼロを与えます。すべてのコンポーネントは他のコンポーネントをインポートできます。リファクタリングは、全体コードベースにおける 1 つの IDE オペレーションです。型チェックは 1 パスでアプリ全体を検証します。統合テストは簡単です。デプロイアーファクトが 1 つしかないためです。多くのエンジニアが見過ごす重要な知見は、マイクロフロントエンドの大部分のメリットは、良好に構造化されたモノリシック内で部分的に達成できるということです:遅く読み込みは小さなバンドルを提供し、モノレポツール (Nx, Turborepo) はチームごとのビルドキャッシュを提供し、CODEOWNERS ファイルは共同の...

Original Content

Micro Frontends, Monolith vs MFE, Hosting Strategies, Cross Communication and Module Federation A deep dive into Monolithic vs Micro Frontend architectures, how multiple MFEs are composed under a single UI shell, communication patterns between MFEs, and a complete walkthrough of Webpack Module Federation with code samples. Monolithic Frontend Architecture What Are Micro Frontends? Monolith vs Micro Frontend Tradeoffs MFE Integration / Composition Approaches Hosting Multiple MFEs Under One UI Cross Communication Between MFEs Module Federation Deep Dive Module Federation 2.0 and Beyond Shared Dependencies and Versioning Routing in Micro Frontends Deployment and CI/CD for MFEs Real World Examples Decision Flowchart, When to Pick What A monolithic frontend is a single, unified codebase where every page, feature, and route lives together. One team (or multiple teams working on the same repo) builds, tests, and deploys the entire application as a single unit. ┌──────────────────────────────────────────────────┐ │ Monolithic SPA │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Header │ │ Sidebar │ │ Footer │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Auth │ │ Product │ │ Cart │ │ │ │ Module │ │ Catalog │ │ Module │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Search │ │ Profile │ │ Orders │ │ │ │ Module │ │ Module │ │ Module │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ Single Build → Single Bundle → Single Deploy │ └──────────────────────────────────────────────────┘ Aspect Detail Codebase Single repository, single build pipeline Deployment All-or-nothing deploy for every change Team Coupling High — every team touches the same code Bundle Size Grows linearly with features (mitigated by code-splitting) Consistency Natural — shared design system, shared state Onboarding Harder as app grows; one massive codebase to learn ┌────────────────────────────────────────────────────────────────────┐ │ Monolith Build & Deploy Flow │ │ │ │ Developer A (Auth) ──┐ │ │ Developer B (Cart) ──┼──▶ Single Repo ──▶ CI Pipeline │ │ Developer C (Search)──┘ (merge to main) │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Lint + Test │ │ │ │ (ALL code) │ │ │ └──────┬──────┘ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Build │ │ │ │ (Entire App)│ │ │ └──────┬──────┘ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Deploy │ │ │ │ (All or │ │ │ │ Nothing) │ │ │ └─────────────┘ │ └────────────────────────────────────────────────────────────────────┘ Key observation: Developer A's one-line auth fix must wait for Developer B's half-finished cart feature to be merged or reverted. Interview Insight: Interviewers love asking "Why not just stay with a monolith?". The truth is: monoliths are not bad. They are the correct starting point for most applications. The problems only emerge at organizational scale, not application scale. A well-structured monolith with good module boundaries, lazy routes, and code-splitting can serve a product for years. The decision to move to micro-frontends is primarily an organizational architecture decision, not a technical one. Small-to-medium app with 1–3 teams Features are tightly coupled (e.g., a dashboard where every widget shares state) Your org doesn't need independent deployment cadences Early-stage products where speed of iteration matters most Why monoliths are actually the right default: A monolith gives you zero coordination overhead. Every component can import any other component. Refactoring is a single IDE operation across the entire codebase. Type checking validates the whole app in one pass. Integration testing is straightforward because there's only one deploy artifact. The critical insight many engineers miss is that most of the benefits attributed to MFEs can be partially achieved within a well-structured monolith: lazy loading gives you small bundles, monorepo tooling (Nx, Turborepo) gives you per-team build caching, and CODEOWNERS files give you code ownership boundaries. The one thing a monolith cannot give you is independent deployment, and that only matters when your release pipeline is genuinely blocked by other teams. Rule of thumb: If your team can ship a feature from code review to production in under a day, your monolith is not the bottleneck. Focus on product features, not architecture migration. App grows → Build times spike (5–15 min+) → Merge conflicts multiply → One bug blocks entire deploy → Teams step on each other's code → Testing surface explodes → Onboarding takes weeks The real problem isn't the code — it's the coupling cascade that happens over time: Year 1: Clean modules, fast builds, small team │ ▼ Year 2: "Just import that util from the other module" └── Shared utilities grow, cross-module imports increase │ ▼ Year 3: "We need a shared context for user data" └── Shared state grows, modules become implicitly coupled │ ▼ Year 4: "Changing the cart broke the search page somehow" └── Implicit dependencies create unpredictable side effects │ ▼ Year 5: "Nobody wants to touch that code" └── Fear-driven development, massive regression test suites, 3-week release cycles, developer frustration Interview Reasoning — "Why does coupling happen in monoliths?" Because nothing prevents it. In a monolith, any file can import any other file. There's no compile-time or deploy-time boundary between "Search" and "Cart". Developers under deadline pressure take shortcuts — they import a utility from another module, share a React context, or add a field to a shared Redux store. Each shortcut is harmless individually, but collectively they create a web of implicit dependencies that makes independent changes impossible. Micro-frontends solve this by creating hard boundaries — separate repos, separate builds, separate deploys. You physically cannot import from another MFE's source code. This forces teams to communicate through explicit contracts (events, APIs, props), which is harder upfront but far more maintainable long-term. Micro-frontends extend the microservices idea to the frontend: split a large monolithic UI into independently developed, tested, deployed, and hosted frontend applications that are composed together in the browser to look like a single cohesive product. "An architectural style where independently deliverable frontend applications are composed into a greater whole." — Martin Fowler The most common mistake in understanding MFEs is thinking of them as horizontal layers (one team does UI, another does API, another does data). Instead, MFEs are vertical slices — each team owns an entire feature end-to-end. ❌ WRONG: Horizontal Layers ✅ CORRECT: Vertical Slices ┌──────────────────────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ UI Team │ │Search│ │Produc│ │ Cart │ ├──────────────────────┤ │ Team │ │ Team │ │ Team │ │ API Team │ │ │ │ │ │ │ ├──────────────────────┤ │ UI │ │ UI │ │ UI │ │ Data Team │ │ API │ │ API │ │ API │ └──────────────────────┘ │ DB │ │ DB │ │ DB │ └──────┘ └──────┘ └──────┘ Teams are organized by Teams own a full feature technical layer → lots from UI to database → of cross-team coordination autonomous delivery Interview Reasoning — "How does MFE relate to microservices?" In backend microservices, each service owns its own database, API, and business logic. MFEs apply the exact same principle to the frontend. Each MFE owns its own UI, its own state, its own routes, and ideally communicates with its own backend microservice. This creates a "full stack vertical" where the Search team owns the search UI (React app), search API (Node/Go service), and search database (Elasticsearch). The team can ship features without coordinating with anyone else — that's the core value proposition. Principle Meaning Why It Matters Team Autonomy Each team owns a vertical slice (feature/domain) end-to-end Eliminates cross-team blocking. A team can ship without waiting for anyone. Technology Agnostic Team A can use React, Team B can use Vue (though uniformity is preferred) Enables incremental migration and lets teams pick the best tool for the job. In practice, most orgs standardize on one framework. Independent Deployment Ship your MFE without coordinating with other teams The #1 reason companies adopt MFEs. Each MFE has its own CI/CD pipeline. Isolation One MFE's bug/crash shouldn't take down others An ErrorBoundary around each MFE ensures a crash in Cart doesn't break Search. No Shared State Prefer explicit contracts (events, APIs) over shared global state Shared state creates implicit coupling — the exact problem MFEs are meant to solve. Deeper Reasoning on Core Principles: Team Autonomy is the most misunderstood principle. It does not mean teams work in complete silos with zero coordination. It means teams can make local decisions (choose a state management library, refactor their component tree, adopt a new testing strategy) without requiring approval from or coordination with other teams. There is still a need for lightweight governance: shared API contracts, a common design system, agreed-upon event schemas, and deployment standards. The difference from a monolith is that this governance is explicit and documented rather than implicit and fragile. Technology Agnostic sounds appealing but is almost always the wrong default. Running React on one MFE and Vue on another means: double the framework bundle size, two sets of tooling expertise needed, two different testing strategies, and significant difficulty sharing a design system. The real value of this principle is for incremental migration — if you're moving from Angular to React over 18 months, you can do it MFE by MFE without a big bang rewrite. No Shared State is the hardest discipline to enforce. Engineers instinctively reach for a global Redux store or React Context because it's the shortest path. In MFEs, you must resist this because shared state is the #1 way to re-introduce the coupling that MFEs are designed to eliminate. Instead, think of each MFE as a separate microservice: it communicates through well-defined APIs (events, props, URL params), not through shared internal state. Interview Tip: When explaining MFEs, always anchor it to the organizational problem first, then the technical solution. "We had 8 teams working in one repo, deploys took 3 weeks, and a bug in checkout blocked the search team's release. We moved to MFEs so each team could ship independently." This shows you understand that architecture decisions are driven by people problems, not technology problems. ┌─────────────────────────────┐ │ App Shell / Host │ │ (Routing, Layout, Auth) │ └─────────┬───────────────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ┌────────▼──────┐ ┌────────▼──────┐ ┌─────────▼─────┐ │ MFE: Search │ │ MFE: Product │ │ MFE: Cart │ │ (Team Alpha) │ │ (Team Beta) │ │ (Team Gamma) │ │ React 18 │ │ React 18 │ │ Vue 3 │ │ Port 3001 │ │ Port 3002 │ │ Port 3003 │ └────────────────┘ └───────────────┘ └───────────────┘ │ │ │ ▼ ▼ ▼ Independent CI/CD Independent CI/CD Independent CI/CD Independent CDN Independent CDN Independent CDN Interview Framing: This is one of the most common system design interview topics. The interviewer wants to see that you can reason about trade-offs, not just list them. For each dimension below, understand the mechanism behind the trade-off — why it exists and when it flips. Dimension Monolith Micro-Frontend Reasoning Team scalability Hard beyond 5–8 devs Scales to dozens of teams In a monolith, merge conflicts and code ownership disputes increase quadratically with team size. MFEs give each team its own sandbox. Deployment All-or-nothing Per-MFE independent A monolith must redeploy everything for a one-line fix. MFEs can ship in minutes because they only build/deploy their own code. Build time Grows with app size Per-MFE (stays small) Webpack/Vite must parse the entire dependency graph. A 500K LoC monolith = 10-15 min builds. A 30K LoC MFE = 30 sec builds. Technology freedom Single stack Mix-and-match (with caution) MFEs allow React 18 in Cart and Vue 3 in Search, but this comes at the cost of duplicate runtimes and inconsistent DX. Most mature orgs standardize. Consistency (UX) Natural Requires shared design system In a monolith, components are shared by default. In MFEs, you must extract a shared design system package and version it carefully. Performance One optimized bundle Risk of duplicate deps, extra requests Tree-shaking works best in a single build. MFEs may load React twice if shared config is wrong (≈140KB wasted). Complexity Low (one app) High (orchestration, contracts, versioning) MFEs add operational complexity: manifest services, shared dep negotiation, cross-MFE communication contracts, integration testing. Testing Single E2E suite Per-MFE + integration tests MFEs can unit/integration test in isolation (fast), but you need an additional E2E layer to test the composed application. Initial setup cost Low High MFE infra (Module Federation config, CI/CD per MFE, manifest service, shared packages) takes 2-4 weeks to set up properly. Fault isolation One bug can break all Bug scoped to one MFE With proper ErrorBoundary wrappers, a crash in Search shows a fallback UI while Cart and Product continue working. Shared state Easy (Redux, context, etc.) Hard (must use explicit contracts) This is the hardest part of MFEs. You can't just useContext() across MFEs. You need event buses, URL params, or props from host. Code reuse Simple imports Shared libraries / packages / remotes In a monolith: import { Button } from '../shared'. In MFEs: publish @org/design-system as a package, or expose via Module Federation. Pain / Overhead │ MFE │ ╱╱╱ Monolith Pain Overhead │ ╱ -------- │ ╱ │ ╱ ────────┼──╱─────────── ← Crossover point │ ╱ (4-6 teams, 100K+ LoC) │╱ MFE Cost │ ──────────── MFE Overhead (relatively flat) │ └────────────────────▶ 1 team 4-6 teams 10+ teams Below the crossover → Monolith is cheaper to maintain Above the crossover → MFE overhead pays for itself Interview Insight: Don't just say "MFEs are better for large teams." Explain the mechanism: in a monolith, coordination cost scales O(n²) with team count (every team's changes can conflict with every other team's changes). MFEs reduce this to O(n) because teams only coordinate at well-defined boundaries (shared contracts, design system versions). ✅ Large org with multiple autonomous teams (5+ teams) ✅ Different teams need different release cadences ✅ Domain boundaries are clear (e.g., e-commerce: search, product, cart, checkout, account) ✅ Parts of the app have different scaling or performance requirements ✅ You need to incrementally migrate a legacy monolith ❌ Small team (< 5 devs) — the overhead isn't worth it ❌ Features are heavily coupled with lots of shared state ❌ Your org doesn't have the DevOps maturity to manage multiple pipelines ❌ You're building a prototype or MVP Most real-world MFE adoption isn't greenfield — it's gradually migrating a monolith. The Strangler Fig pattern (named after a tree that gradually wraps and replaces its host) is the proven approach: Phase 1: Monolith serves everything ┌─────────────────────────────────────────┐ │ MONOLITH (everything) │ │ Search | Product | Cart | Checkout │ └─────────────────────────────────────────┘ │ Phase 2: Add App Shell + extract first MFE ┌─────────────────────────────────────────┐ │ APP SHELL │ │ /search → ┌─────────────┐ │ │ │ Search MFE │ (new) │ │ └─────────────┘ │ │ /* else → ┌────────────────────┐ │ │ │ MONOLITH (legacy) │ │ │ │ Product|Cart|Chkout│ │ │ └────────────────────┘ │ └─────────────────────────────────────────┘ │ Phase 3: Extract more MFEs over months ┌─────────────────────────────────────────┐ │ APP SHELL │ │ /search → Search MFE │ │ /product → Product MFE (extracted) │ │ /cart → Cart MFE (extracted) │ │ /* else → Monolith (shrinking) │ └─────────────────────────────────────────┘ │ Phase 4: Monolith fully decomposed ┌─────────────────────────────────────────┐ │ APP SHELL │ │ /search → Search MFE │ │ /product → Product MFE │ │ /cart → Cart MFE │ │ /checkout → Checkout MFE │ │ /account → Account MFE │ │ │ │ ✘ Monolith is gone │ └─────────────────────────────────────────┘ Interview Reasoning: The Strangler Fig approach is safe because at every phase, the system is fully functional. You never have a "big bang" migration weekend. If the new Search MFE has bugs, you can route /search back to the monolith with a single config change. This de-risks the entire migration. Practical Reasoning for Migration Teams: The Strangler Fig pattern succeeds where "big bang" rewrites fail because it aligns incentives with reality. Consider the psychology: a rewrite project that's "80% done" delivers zero business value until it's 100% complete and stable. Meanwhile, the legacy app still needs bug fixes and feature requests, so the team is split between two codebases. Eventually, the rewrite gets cancelled or the legacy codebase drifts so far that the rewrite is outdated before launch. With Strangler Fig, each phase delivers immediate, measurable value: after Phase 2, the Search page is already faster, more accessible, or easier to maintain. The team can celebrate a win, stakeholders see progress, and there's always a working system. If organizational priorities shift and the migration stalls at Phase 3, you still have 3 modernized MFEs running alongside the legacy monolith — not a half-finished rewrite that serves nobody. Key technical consideration: The hardest part of Strangler Fig migration is shared authentication and layout. The monolith and new MFEs must share the same login session (via shared cookies or a token in localStorage) and maintain a consistent header/footer across both old and new pages. Prioritize building the App Shell and auth integration first — this is the foundation that makes everything else possible. There are several ways to compose micro-frontends into a single user experience. Each has different trade-offs around performance, isolation, and complexity. Interview Context: This is the most important section for system design interviews. When an interviewer asks "How would you architect a micro-frontend system?", they want to see that you know multiple composition strategies and can reason about which one fits a given scenario. Don't just default to Module Federation — show you understand the full spectrum. Build-Time Runtime (tight coupling, (loose coupling, best performance) more flexibility) │ │ ▼ ▼ ┌────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌─────────┐ │ npm │ │ Module │ │ JS │ │ Web │ │ iframes │ │packages│ │ Federat- │ │ Dynamic │ │ Compo- │ │ │ │ │ │ ion │ │ Remotes │ │ nents │ │ │ └────────┘ └─────────┘ └─────────┘ └────────┘ └─────────┘ Less autonomy More autonomy Better perf More isolation Simpler setup More complexity MFEs are published as npm packages and the host app installs them as dependencies. // host/package.json { "dependencies": { "@org/mfe-search": "^2.1.0", "@org/mfe-product": "^3.0.0", "@org/mfe-cart": "^1.5.0" } } // host/src/App.jsx import Search from '@org/mfe-search'; import Product from '@org/mfe-product'; import Cart from '@org/mfe-cart'; function App() { return ( ); } Pros Cons Simple to set up Loses independent deployment (host must rebuild) Strong typing across MFEs Version coupling — lock-step releases Single optimized bundle Not truly "micro" — just well-organized monolith Verdict: Good for shared component libraries, not ideal for true MFE autonomy. Interview Reasoning — "Why isn't npm-based integration considered true MFE?" Because the core promise of micro-frontends is independent deployment. With npm packages, every time the Search team ships a new version, the Host must: (1) bump the dependency version, (2) rebuild, (3) run its own CI/CD, (4) deploy. This means the Host team becomes a bottleneck. In a 10-team org, the Host could be rebuilding 5 times a day just to pick up other teams' changes. It's still a monolithic deployment pipeline with extra steps. However, this approach works great for shared component libraries (design systems, utility packages) where you want the consumer to explicitly opt-in to new versions. Each MFE is loaded inside an . ... Pros Cons Complete isolation (CSS, JS, DOM) Poor UX (no shared scrolling, awkward resizing) Technology agnostic Cross-iframe communication is clunky (postMessage) Simple mental model SEO-unfriendly Security sandbox Performance overhead (each iframe is a full browser context) Cannot share dependencies (duplicate React, etc.) Verdict: Good for embedding third-party widgets or legacy migration, not for primary app composition. Interview Reasoning — "When would you actually use iframes?" Despite their bad reputation, iframes are the only way to get complete isolation between MFEs. This matters in specific scenarios: Third-party widget embedding (Intercom chat, payment forms) — you don't control the code, so you need full sandboxing. Legacy migration — Wrap the old jQuery monolith in an iframe while building new MFEs in React. Users see one app. Security-critical sections — A banking app might iframe the payment processing page so a XSS in the main app can't steal card data (with proper sandbox attributes). The performance overhead is real: each iframe creates a separate browsing context with its own document, CSS engine, JavaScript heap, and event loop. For 3 iframes, that's roughly 4x the memory usage of a single-page app. The host loads each MFE's JS bundle at runtime, and each MFE exports a mount(container) function. Host App Shell │ ├── Fetch manifest.json → { search: "https://cdn/.../search.js" } │ ├──