Micro-frontends với Module Federation: Chia nhỏ monolith
Hướng dẫn thực tế triển khai micro-frontends với Webpack Module Federation và Vite federation plugin — cách chia nhỏ monolith frontend mà không mất DX.
Khi frontend monolith vượt quá 500K LOC và 10+ teams cùng contribute, build time 15 phút, deploy queue dài vô tận — đó là lúc cần micro-frontends. Module Federation là cách tiếp cận thực tế nhất hiện tại.
Module Federation là gì?
Module Federation cho phép một JavaScript application dynamically load code từ application khác tại runtime. Không cần build chung, không cần npm package. App A expose component, App B consume trực tiếp qua URL.
Setup với Webpack 5
Giả sử bạn có monolith e-commerce cần tách thành: Shell (layout), Product (catalog), Cart, Checkout.
// product-app/webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "productApp",
filename: "remoteEntry.js",
exposes: {
"./ProductList": "./src/components/ProductList",
"./ProductDetail": "./src/components/ProductDetail",
"./useProduct": "./src/hooks/useProduct",
},
shared: {
react: { singleton: true, requiredVersion: "^19.0.0" },
"react-dom": { singleton: true, requiredVersion: "^19.0.0" },
},
}),
],
};
// shell-app/webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "shell",
remotes: {
productApp: "productApp@https://product.example.com/remoteEntry.js",
cartApp: "cartApp@https://cart.example.com/remoteEntry.js",
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
}),
],
};
Consume remote modules
Trong shell app, import remote components như local modules:
// shell-app/src/pages/Products.tsx
import React, { Suspense, lazy } from "react";
const ProductList = lazy(() => import("productApp/ProductList"));
const CartWidget = lazy(() => import("cartApp/CartWidget"));
export function ProductsPage() {
return (
<div className="grid grid-cols-12">
<div className="col-span-9">
<Suspense fallback={<ProductsSkeleton />}>
<ProductList
onAddToCart={(productId) => {
// Cross-app communication qua custom events
window.dispatchEvent(
new CustomEvent("cart:add", { detail: { productId } })
);
}}
/>
</Suspense>
</div>
<div className="col-span-3">
<Suspense fallback={<CartSkeleton />}>
<CartWidget />
</Suspense>
</div>
</div>
);
}
Remote modules load tại runtime. Nếu product-app deploy version mới, shell-app tự động nhận code mới mà không cần redeploy.
Vite Federation Plugin
Nếu dùng Vite, @module-federation/vite cung cấp API tương tự. Config gần giống Webpack nhưng integrate vào Vite pipeline. Performance build tốt hơn nhiều so với Webpack.
Cross-app communication
Đây là phần khó nhất. Các patterns phổ biến:
1. Custom Events — Đơn giản, loosely coupled. Dùng cho one-way notifications (cart updated, user logged in).
2. Shared state store — Zustand store expose qua Module Federation. Cả shell và remotes access cùng store instance.
3. Props drilling — Shell truyền props xuống remote components. Đơn giản nhưng tight coupling.
4. URL-based — Dùng URL params/search params cho state sharing. Works với mọi framework.
Khi nào KHÔNG nên dùng micro-frontends
Team nhỏ (<5 devs) — Overhead vượt quá benefit. Monolith with good code organization đủ rồi.
Shared UI nhiều — Nếu 80% UI là shared components, micro-frontends tạo ra duplication không cần thiết.
SEO-critical pages — Dynamic loading remote modules khó optimize cho SSR/SEO. Cần extra setup.
Khi nào NÊN dùng
Multiple teams, independent deploy cycles — Mỗi team own một domain, deploy khi muốn.
Different tech stacks — Team A dùng React, Team B dùng Vue. Module Federation hỗ trợ cả hai.
Brownfield migration — Migrate dần từ legacy app sang modern stack, page by page.
Kết luận
Micro-frontends với Module Federation là giải pháp thực tế cho large-scale frontend. Nhưng nó đi kèm complexity: versioning, shared dependencies, cross-app state, monitoring. Chỉ dùng khi organizational problem (nhiều teams, independent releases) lớn hơn technical complexity. Đừng dùng vì trendy.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!