Micro-Frontend 2026: Chia để trị Frontend với Module Federation 2.0
Posted on: 4/18/2026 5:13:44 AM
Table of contents
- 1. Bài toán Scale Frontend — Khi Monolith Frontend trở thành nút thắt
- 2. Micro-Frontend — Chia để trị, nhưng đúng cách
- 3. Các mô hình tích hợp Micro-Frontend
- 4. Module Federation 2.0 — Deep Dive
- 5. Rspack — Build 10x nhanh hơn Webpack
- 6. Thực hành: Micro-Frontend với Vue 3 + Rspack
- 7. Giao tiếp giữa các Micro-Frontend
- 8. Chiến lược Shared Dependencies
- 9. CI/CD và Deployment Strategy
- 10. CSS Isolation — Tránh style conflict
- 11. Performance — Tránh Micro-Frontend trở thành Macro-Problem
- 12. Khi nào nên (và không nên) dùng Micro-Frontend
- Tổng kết
- Tài liệu tham khảo
Khi frontend của bạn vượt quá 200.000 dòng code, 15 developer cùng commit vào một repo, và mỗi lần build mất 8 phút — bạn đang đối mặt với bài toán scale frontend. Micro-Frontend không phải buzzword mới, nhưng với Module Federation 2.0 đạt stable release tháng 4/2026 và Rspack cho tốc độ build nhanh gấp 10 lần Webpack, kiến trúc này cuối cùng đã sẵn sàng cho production thực sự.
1. Bài toán Scale Frontend — Khi Monolith Frontend trở thành nút thắt
Hãy tưởng tượng một ứng dụng SaaS với các module: Dashboard, User Management, Billing, Analytics, Notification Center. Tất cả nằm trong một repo Vue/React duy nhất. Ban đầu mọi thứ ổn, nhưng khi team mở rộng:
- Merge conflict liên tục: 5 team cùng sửa
package.json, shared components, và router config - Build time phi tuyến: thêm 1 module mới khiến Webpack build chậm thêm 30% vì phải resolve lại toàn bộ dependency graph
- Deploy coupling: team Billing fix 1 bug nhỏ → phải deploy lại toàn bộ app 500K dòng code, ảnh hưởng Dashboard và Analytics
- Technology lock-in: muốn migrate từ Vue 2 sang Vue 3 phải làm "big bang" — không thể migrate từng module
Monolith Frontend ≠ Bad Architecture
Nếu team bạn dưới 5 người và app dưới 50K LOC, monolith frontend vẫn là lựa chọn tốt nhất. Micro-Frontend giải quyết vấn đề tổ chức team (Conway's Law) chứ không phải vấn đề kỹ thuật đơn thuần. Đừng over-engineer khi chưa cần.
2. Micro-Frontend — Chia để trị, nhưng đúng cách
Micro-Frontend là kiến trúc chia một ứng dụng web lớn thành nhiều ứng dụng nhỏ hơn (gọi là remote hoặc fragment), mỗi ứng dụng được phát triển, test và deploy độc lập bởi một team riêng biệt, sau đó được tổ hợp (compose) thành một trải nghiệm người dùng thống nhất.
graph TB
subgraph "Shell / Host App"
Shell["🏠 Shell Application
Router + Layout + Auth"]
end
subgraph "Team Products"
MF1["📦 Product Catalog
Vue 3.6 + Rspack"]
end
subgraph "Team Checkout"
MF2["🛒 Checkout Flow
Vue 3.6 + Vite"]
end
subgraph "Team Account"
MF3["👤 User Account
React 19"]
end
subgraph "Shared"
DS["🎨 Design System"]
Auth["🔐 Auth Module"]
end
Shell --> MF1
Shell --> MF2
Shell --> MF3
MF1 --> DS
MF2 --> DS
MF3 --> DS
MF1 --> Auth
MF2 --> Auth
MF3 --> Auth
style Shell fill:#e94560,stroke:#fff,color:#fff
style MF1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style MF2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style MF3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style DS fill:#2c3e50,stroke:#fff,color:#fff
style Auth fill:#2c3e50,stroke:#fff,color:#fff
Kiến trúc Micro-Frontend: Shell App orchestrate các remote app độc lập
2.1. Năm nguyên tắc cốt lõi
- Team Autonomy: mỗi team sở hữu toàn bộ lifecycle từ dev → test → deploy
- Technology Agnostic: team A dùng Vue, team B dùng React — không ép buộc
- Independent Deployment: deploy module Billing không ảnh hưởng Dashboard
- No Shared State: các micro-frontend giao tiếp qua contract rõ ràng, không chia sẻ global state
- Resilient by Default: module Analytics crash không kéo sập toàn bộ app
3. Các mô hình tích hợp Micro-Frontend
Có 4 cách chính để tổ hợp các micro-frontend thành một ứng dụng thống nhất:
| Mô hình | Cách hoạt động | Ưu điểm | Nhược điểm | Phù hợp khi |
|---|---|---|---|---|
| Build-time (npm packages) | Mỗi MF là npm package, host app install và build cùng | Đơn giản, type-safe | Deploy coupling — update 1 MF phải rebuild host | Team nhỏ, ít thay đổi |
| Runtime — Module Federation | Host load remote bundle qua HTTP tại runtime | Deploy độc lập, shared deps | Phức tạp hơn, cần version strategy | Team lớn, deploy thường xuyên |
| Server-Side Composition | Server stitches HTML fragments từ nhiều service | SEO tốt, fast TTFB | Khó interactive, phức tạp infra | Content-heavy sites, e-commerce |
| iframe | Mỗi MF chạy trong iframe riêng | Isolation hoàn toàn | Performance kém, UX rời rạc | Legacy integration, |
Runtime Integration là xu hướng 2026
Với Module Federation 2.0 đạt stable, runtime integration đã vượt qua các hạn chế cũ (thiếu type safety, version conflict). Hầu hết các dự án mới trong 2026 nên chọn Module Federation làm điểm xuất phát.
4. Module Federation 2.0 — Deep Dive
Module Federation ban đầu là tính năng của Webpack 5 (2020), cho phép một JavaScript application load code từ application khác tại runtime. Phiên bản 2.0 (stable tháng 4/2026) là bước tiến lớn với 3 thay đổi mang tính cách mạng:
4.1. Decoupled Runtime — Không còn phụ thuộc Webpack
MF 2.0 tách hoàn toàn runtime khỏi bundler. Runtime layer trở thành một module độc lập, chạy nhất quán trên bất kỳ build tool nào:
graph LR
subgraph "Module Federation 2.0 Runtime"
RT["MF Runtime
(Bundler-agnostic)"]
end
subgraph "Build Tools"
WP["Webpack 5"]
RS["Rspack"]
VI["Vite"]
RU["Rollup / Rolldown"]
RB["Rsbuild"]
end
subgraph "Frameworks"
NX["Next.js"]
NU["Nuxt 4"]
MJ["Modern.js"]
end
WP --> RT
RS --> RT
VI --> RT
RU --> RT
RB --> RT
RT --> NX
RT --> NU
RT --> MJ
style RT fill:#e94560,stroke:#fff,color:#fff
style WP fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
style RS fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
style VI fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
style RU fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
style RB fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
MF 2.0 Runtime hoạt động độc lập với build tool — team có thể dùng Webpack, Rspack hay Vite tùy ý
Điều này giải quyết vấn đề lớn nhất của MF 1.0: mọi remote app phải dùng chung Webpack. Với MF 2.0, host app dùng Rspack và remote app dùng Vite vẫn hoạt động hoàn hảo.
4.2. Dynamic Type Hints — TypeScript không cần build lại
MF 2.0 tự động generate và load TypeScript types từ remote modules tại development time, cho trải nghiệm tương tự npm link nhưng không cần link:
// host-app/src/bootstrap.ts
// Types từ remote "checkout" được tự động generate
import { CartSummary } from 'checkout/CartSummary';
// ↑ Full type inference, autocomplete, go-to-definition
// Khi remote app update interface → types tự hot-reload
// Không cần publish npm package, không cần rebuild host
Cơ chế hoạt động: MF 2.0 plugin chạy một background process, watch remote manifest, tự generate .d.ts vào node_modules/@mf-types/. Developer thấy type error ngay khi remote thay đổi interface — phát hiện breaking change trước khi deploy.
4.3. Runtime Plugins — Mở rộng hành vi tại runtime
MF 2.0 giới thiệu hệ thống plugin cho phép can thiệp vào quá trình load remote module:
import { FederationRuntimePlugin } from '@module-federation/runtime';
const AuthPlugin: () => FederationRuntimePlugin = () => ({
name: 'auth-plugin',
// Gắn auth token vào mọi request load remote
fetch(url, options) {
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${getAccessToken()}`
}
});
},
// Fallback khi remote không khả dụng
errorLoadRemote({ id, error }) {
console.error(`Failed to load ${id}:`, error);
return () => import('./fallback/MaintenancePage.vue');
}
});
Plugin system cho phép: retry logic, A/B testing (load remote version A hoặc B), canary deployment, telemetry tracking, và circuit breaker pattern.
5. Rspack — Build 10x nhanh hơn Webpack
Một trong những rào cản lớn nhất của Micro-Frontend là developer experience. Khi phải chạy 5 remote app + 1 host app cùng lúc, mỗi app build 30 giây bằng Webpack → tốn 3 phút chỉ để start dev environment. Rspack (viết bằng Rust, do ByteDance phát triển) giải quyết triệt để vấn đề này.
Rspack không phải bundler hoàn toàn mới — nó tương thích gần như hoàn toàn với Webpack config, plugins, và loaders. Việc migrate từ Webpack sang Rspack thường chỉ mất vài giờ thay vì vài tuần.
5.1. Cấu hình Rspack + Module Federation 2.0
// rspack.config.js — Host App
const { ModuleFederationPlugin } = require('@module-federation/enhanced/rspack');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
// Remote apps — URL trỏ tới manifest, không phải bundle
productCatalog: 'productCatalog@https://product.example.com/mf-manifest.json',
checkout: 'checkout@https://checkout.example.com/mf-manifest.json',
userAccount: 'userAccount@https://account.example.com/mf-manifest.json',
},
shared: {
vue: { singleton: true, requiredVersion: '^3.6.0' },
pinia: { singleton: true, requiredVersion: '^3.0.0' },
'vue-router': { singleton: true, requiredVersion: '^4.5.0' },
},
}),
],
};
// rspack.config.js — Remote App (Product Catalog)
const { ModuleFederationPlugin } = require('@module-federation/enhanced/rspack');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'productCatalog',
exposes: {
// Expose specific components, không phải toàn bộ app
'./ProductList': './src/components/ProductList.vue',
'./ProductDetail': './src/components/ProductDetail.vue',
'./SearchBar': './src/components/SearchBar.vue',
},
shared: {
vue: { singleton: true, requiredVersion: '^3.6.0' },
pinia: { singleton: true, requiredVersion: '^3.0.0' },
},
}),
],
};
6. Thực hành: Micro-Frontend với Vue 3 + Rspack
6.1. Shell App — Orchestrator trung tâm
Shell app (hay host app) chịu trách nhiệm: routing cấp cao, layout chung, authentication, và load các remote app theo demand.
// shell/src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('../layouts/MainLayout.vue'),
children: [
{
path: 'products/:pathMatch(.*)*',
component: () => import('productCatalog/ProductList'),
// ↑ Load từ remote app tại runtime
},
{
path: 'checkout/:pathMatch(.*)*',
component: () => import('checkout/CheckoutFlow'),
},
{
path: 'account/:pathMatch(.*)*',
component: () => import('userAccount/AccountDashboard'),
},
],
},
],
});
<!-- shell/src/layouts/MainLayout.vue -->
<template>
<div class="app-shell">
<AppHeader />
<nav>
<router-link to="/products">Sản phẩm</router-link>
<router-link to="/checkout">Thanh toán</router-link>
<router-link to="/account">Tài khoản</router-link>
</nav>
<main>
<ErrorBoundary>
<Suspense>
<router-view />
<template #fallback>
<LoadingSkeleton />
</template>
</Suspense>
</ErrorBoundary>
</main>
</div>
</template>
ErrorBoundary là bắt buộc
Luôn wrap remote component trong ErrorBoundary + Suspense. Khi remote app bị lỗi (network fail, version mismatch), ErrorBoundary hiển thị fallback UI thay vì crash toàn bộ shell app. Đây là nguyên tắc "Resilient by Default" trong thực tế.
6.2. Remote App — Chạy độc lập và như micro-frontend
Mỗi remote app phải hoạt động được ở 2 chế độ: standalone (cho dev/test độc lập) và federated (khi load từ shell):
// product-catalog/src/bootstrap.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
const mount = (el: string | HTMLElement, opts?: { basePath?: string }) => {
const app = createApp(App);
app.use(createPinia());
if (opts?.basePath) {
// Federated mode: shell truyền basePath
router.replace(opts.basePath);
}
app.use(router);
app.mount(typeof el === 'string' ? el : el);
return app;
};
// Standalone mode: mount trực tiếp
const rootEl = document.getElementById('app');
if (rootEl) {
mount(rootEl);
}
export { mount };
7. Giao tiếp giữa các Micro-Frontend
Đây là phần phức tạp nhất và cũng dễ mắc sai lầm nhất. Nguyên tắc vàng: các micro-frontend KHÔNG chia sẻ state trực tiếp. Thay vào đó, sử dụng một trong các pattern sau:
7.1. Custom Events — Đơn giản và hiệu quả
// shared-contracts/events.ts (npm package dùng chung)
export interface CartUpdatedEvent {
type: 'cart:updated';
payload: { itemCount: number; totalAmount: number };
}
export interface UserLoggedInEvent {
type: 'user:loggedIn';
payload: { userId: string; displayName: string };
}
export type MicroFrontendEvent = CartUpdatedEvent | UserLoggedInEvent;
// Product Catalog — publish event
export function addToCart(product: Product) {
// ... business logic
window.dispatchEvent(
new CustomEvent('mf:event', {
detail: {
type: 'cart:updated',
payload: { itemCount: cart.length, totalAmount: total }
} satisfies CartUpdatedEvent
})
);
}
// Shell App — subscribe
window.addEventListener('mf:event', ((e: CustomEvent<MicroFrontendEvent>) => {
switch (e.detail.type) {
case 'cart:updated':
headerCartBadge.value = e.detail.payload.itemCount;
break;
case 'user:loggedIn':
currentUser.value = e.detail.payload;
break;
}
}) as EventListener);
7.2. Shared API Layer — Cho dữ liệu phức tạp
Khi cần chia sẻ dữ liệu phức tạp hơn (user session, feature flags, theme), dùng một shared API layer được expose qua Module Federation:
// shell/src/shared/api.ts — expose qua MF
import { reactive, readonly } from 'vue';
const state = reactive({
user: null as User | null,
theme: 'light' as 'light' | 'dark',
featureFlags: {} as Record<string, boolean>,
});
export const shellApi = {
getUser: () => readonly(state).user,
getTheme: () => readonly(state).theme,
getFeatureFlag: (key: string) => state.featureFlags[key] ?? false,
// Event-based notification khi state thay đổi
onUserChange: (cb: (user: User | null) => void) => {
watch(() => state.user, cb);
},
};
Anti-pattern: Shared Pinia Store
Đừng bao giờ share một Pinia store instance giữa các micro-frontend. Điều này tạo implicit coupling — khi team A thay đổi store schema, team B bị break mà không biết. Luôn giao tiếp qua explicit contract (events hoặc exposed API).
8. Chiến lược Shared Dependencies
Module Federation cho phép các app chia sẻ dependencies thay vì bundle riêng. Nhưng nếu không cấu hình đúng, shared dependencies sẽ trở thành nguồn bug khó debug nhất.
| Strategy | Config | Hành vi | Khi nào dùng |
|---|---|---|---|
| Singleton | singleton: true | Chỉ load 1 version duy nhất, version cao nhất thắng | Vue, React, Pinia — libraries giữ global state |
| Eager | eager: true | Bundle vào initial chunk, không lazy load | Lib cần trước khi remote ready (polyfills) |
| Required Version | requiredVersion: '^3.6.0' | Warning/error nếu version không match | Tránh major version conflict |
| Strict Version | strictVersion: true | Throw error thay vì warning khi mismatch | Critical libs (auth, crypto) |
// Cấu hình shared dependencies chuẩn cho Vue ecosystem
shared: {
// Framework core — PHẢI singleton
'vue': { singleton: true, requiredVersion: '^3.6.0' },
'vue-router': { singleton: true, requiredVersion: '^4.5.0' },
'pinia': { singleton: true, requiredVersion: '^3.0.0' },
// UI library — singleton để tránh duplicate CSS
'@company/design-system': { singleton: true },
// Utility libs — KHÔNG singleton, mỗi app tự bundle
// lodash, dayjs, axios: để mặc định
}
graph TB
subgraph "Browser Runtime"
subgraph "Shared Scope"
Vue["vue@3.6.2
(singleton)"]
Router["vue-router@4.5.1
(singleton)"]
DS["Design System@2.1
(singleton)"]
end
subgraph "Shell Bundle"
ShellCode["Shell App Code"]
end
subgraph "Product Bundle"
ProdCode["Product Code"]
Lodash1["lodash@4.17
(own copy)"]
end
subgraph "Checkout Bundle"
CheckCode["Checkout Code"]
Lodash2["lodash@4.17
(own copy)"]
end
end
ShellCode --> Vue
ProdCode --> Vue
CheckCode --> Vue
ShellCode --> Router
ProdCode --> DS
CheckCode --> DS
style Vue fill:#e94560,stroke:#fff,color:#fff
style Router fill:#e94560,stroke:#fff,color:#fff
style DS fill:#e94560,stroke:#fff,color:#fff
style Lodash1 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
style Lodash2 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
Shared Scope: Vue và Router load 1 lần, utility libs (lodash) mỗi app bundle riêng
9. CI/CD và Deployment Strategy
Lợi ích lớn nhất của Micro-Frontend là independent deployment. Mỗi team có pipeline riêng, deploy khi sẵn sàng mà không cần coordinate với team khác.
graph LR
subgraph "Team Product"
P1["Push to main"] --> P2["Build + Test"]
P2 --> P3["Deploy to CDN
product.cdn.com"]
P3 --> P4["Update manifest"]
end
subgraph "Team Checkout"
C1["Push to main"] --> C2["Build + Test"]
C2 --> C3["Deploy to CDN
checkout.cdn.com"]
C3 --> C4["Update manifest"]
end
subgraph "Shell App"
S1["Reads manifest
at runtime"]
S1 --> S2["Loads latest
remote bundle"]
end
P4 --> S1
C4 --> S1
style P3 fill:#4CAF50,stroke:#fff,color:#fff
style C3 fill:#4CAF50,stroke:#fff,color:#fff
style S1 fill:#e94560,stroke:#fff,color:#fff
Independent deployment: mỗi team deploy riêng, shell app tự load version mới nhất qua manifest
9.1. Versioning Strategy
Có 2 cách quản lý version cho remote modules:
Dynamic manifest (khuyến nghị): Remote app cập nhật mf-manifest.json khi deploy. Shell app luôn load manifest mới nhất → tự động nhận version mới. Không cần redeploy shell.
// https://product.example.com/mf-manifest.json
{
"id": "productCatalog",
"name": "productCatalog",
"metaData": {
"buildHash": "a3f7c2b",
"version": "2.14.0",
"buildTime": "2026-04-18T10:30:00Z"
},
"exposes": {
"./ProductList": {
"assets": { "js": { "async": ["src_components_ProductList_vue.js"] } }
}
},
"shared": [
{ "name": "vue", "version": "3.6.2", "scope": "default" }
]
}
Pinned version: Shell config trỏ đến version cụ thể. An toàn hơn nhưng cần update shell config mỗi lần remote deploy. Phù hợp cho môi trường production yêu cầu kiểm soát chặt.
9.2. Canary Deployment với MF Runtime Plugin
// shell/src/plugins/canary.ts
import { FederationRuntimePlugin } from '@module-federation/runtime';
const CanaryPlugin: () => FederationRuntimePlugin = () => ({
name: 'canary-plugin',
beforeRequest(args) {
const { id } = args;
// 5% traffic → canary version
if (isInCanaryGroup(getUserId())) {
args.options.remotes = args.options.remotes.map(remote => {
if (remote.name === id) {
return {
...remote,
entry: remote.entry.replace('/stable/', '/canary/'),
};
}
return remote;
});
}
return args;
},
});
10. CSS Isolation — Tránh style conflict
Khi nhiều micro-frontend cùng render trên 1 page, CSS conflict là vấn đề thường gặp nhất. Các giải pháp:
| Giải pháp | Cách hoạt động | Pros | Cons |
|---|---|---|---|
| CSS Modules | Class name được hash unique (.btn_a3f7c) | Zero conflict, tree-shakable | Dynamic class name khó debug |
| Vue Scoped Styles | Attribute selector (.btn[data-v-7ba5bd90]) | Native Vue, dễ dùng | Specificity vẫn có thể leak |
| CSS-in-JS | Style inject tại runtime | Full isolation | Runtime cost, bundle size tăng |
| CSS Layers | @layer mf-product { ... } | Native CSS, rõ ràng priority | Cần coordinate layer order |
| Shadow DOM | Web Component encapsulation | Complete isolation | Khó theme, performance overhead |
Khuyến nghị 2026: CSS Modules + Design Token
Dùng CSS Modules cho isolation, kết hợp Design Tokens (CSS custom properties) từ shared design system cho consistency. Mỗi MF import @company/tokens — chỉ chứa CSS variables, không chứa component styles.
/* @company/tokens/variables.css */
:root {
--color-primary: #e94560;
--color-surface: #ffffff;
--spacing-md: 16px;
--radius-lg: 12px;
--font-body: 'Inter', system-ui, sans-serif;
}
/* product-catalog/src/components/ProductCard.module.css */
.card {
background: var(--color-surface);
border-radius: var(--radius-lg);
padding: var(--spacing-md);
}
.title {
color: var(--color-primary);
font-family: var(--font-body);
}
11. Performance — Tránh Micro-Frontend trở thành Macro-Problem
Micro-Frontend nếu không cẩn thận sẽ tăng bundle size tổng (mỗi app bundle riêng), tăng số request (load nhiều manifest + chunk), và giảm INP (hydration overhead). Các chiến lược tối ưu:
11.1. Preloading Remote Modules
// Shell app — preload remote khi user hover vào nav link
import { preloadRemote } from '@module-federation/runtime';
function onNavHover(route: string) {
switch (route) {
case '/products':
preloadRemote([{ name: 'productCatalog', expose: './ProductList' }]);
break;
case '/checkout':
preloadRemote([{ name: 'checkout', expose: './CheckoutFlow' }]);
break;
}
}
11.2. Islands Architecture — Chỉ hydrate khi cần
Thay vì hydrate toàn bộ page, chỉ hydrate micro-frontend "island" mà user đang tương tác. Kết hợp với IntersectionObserver để lazy-hydrate khi element vào viewport:
<!-- LazyMicroFrontend.vue -->
<template>
<div ref="container">
<component :is="RemoteComponent" v-if="isVisible" />
<slot v-else name="placeholder">
<div class="skeleton" />
</slot>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, defineAsyncComponent, shallowRef } from 'vue';
const props = defineProps<{
remoteName: string;
exposedModule: string;
}>();
const container = ref<HTMLElement>();
const isVisible = ref(false);
const RemoteComponent = shallowRef(
defineAsyncComponent(() => import(`${props.remoteName}/${props.exposedModule}`))
);
onMounted(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
isVisible.value = true;
observer.disconnect();
}
},
{ rootMargin: '200px' }
);
if (container.value) observer.observe(container.value);
});
</script>
12. Khi nào nên (và không nên) dùng Micro-Frontend
graph TD
Q1{"Frontend team
≥ 8 người?"}
Q1 -->|Không| NO["❌ Giữ Monolith
Micro-FE là overkill"]
Q1 -->|Có| Q2{"Nhiều team cùng
deploy 1 app?"}
Q2 -->|Không| NO2["❌ Chia repo
nhưng không cần MF"]
Q2 -->|Có| Q3{"Deploy frequency
≥ 2 lần/tuần
mỗi team?"}
Q3 -->|Không| MAYBE["⚠️ Cân nhắc
Modular Monolith
trước"]
Q3 -->|Có| YES["✅ Micro-Frontend
là lựa chọn đúng"]
style YES fill:#4CAF50,stroke:#fff,color:#fff
style NO fill:#f8f9fa,stroke:#e0e0e0,color:#888
style NO2 fill:#f8f9fa,stroke:#e0e0e0,color:#888
style MAYBE fill:#ff9800,stroke:#fff,color:#fff
Decision tree: Bạn có thực sự cần Micro-Frontend?
Dấu hiệu nên áp dụng
- Nhiều team (3+) cùng phát triển 1 frontend application
- Deploy frequency cao — mỗi team cần deploy độc lập ít nhất 2 lần/tuần
- Cần migrate dần công nghệ (Vue 2 → Vue 3, hoặc Vue → React cho 1 module)
- App đủ lớn (100K+ LOC) và có boundary rõ ràng giữa các domain
Anti-patterns cần tránh
- Nano-Frontend: chia quá nhỏ — mỗi component là 1 remote app → overhead lớn hơn lợi ích
- Shared Everything: share quá nhiều dependencies → quay lại monolith coupling
- Cross-MF Database Queries: remote app truy cập API/data của remote app khác trực tiếp → tạo distributed monolith
- Ignoring UX Consistency: mỗi team tự design component → user thấy app "rời rạc"
Micro-Frontend Tax
Micro-Frontend thêm overhead đáng kể: shared dependency management, contract testing giữa teams, CI/CD pipeline phức tạp hơn, monitoring phải track từng remote module. Chỉ "trả thuế" này khi lợi ích từ team autonomy và independent deployment thực sự lớn hơn chi phí.
Tổng kết
Micro-Frontend trong 2026 không còn là thí nghiệm. Với Module Federation 2.0 (stable, bundler-agnostic, dynamic types), Rspack (build 10x nhanh hơn Webpack), và các pattern đã được kiểm chứng tại các enterprise lớn — kiến trúc này đã sẵn sàng cho production. Điều quan trọng nhất vẫn là: đánh giá đúng bài toán trước khi chọn giải pháp. Nếu team bạn đang chật vật với merge conflict, deploy coupling, và build time phi lý trên một monolith frontend lớn — Micro-Frontend với Module Federation 2.0 chính là lối ra.
Tài liệu tham khảo
- Module Federation Official Documentation
- Module Federation 2.0 Reaches Stable Release — InfoQ (04/2026)
- Rspack — Module Federation Guide
- Micro-frontends 2026: Module Federation 3.0 & Native ESM Federation
- How Microfrontends Work: From iframes to Module Federation — freeCodeCamp
- Micro Frontends — Martin Fowler
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.