Thử thách lớn nhất của MFE: Làm sao để các team làm việc độc lập mà giao diện vẫn "như một" và dữ liệu vẫn "nhất quán"?
1. Phân tích thiết kế: State Isolation vs Shared Store
Cách làm truyền thống: Shared Redux Store (Global State)
Tất cả các MFE dùng chung một Store Redux khổng lồ được khởi tạo tại App Shell.
Nhược điểm:
- Dependency Hell: Team A thay đổi cấu trúc dữ liệu giỏ hàng làm Team B bị sập logic hiển thị badge ở Header.
- Micro-Monolith: Bạn chỉ đang chia nhỏ UI, nhưng thực tế toàn bộ logic vẫn bị trói buộc vào một "cục" state duy nhất.
- Performance: Một thay đổi nhỏ ở store chung kích hoạt re-render trên toàn bộ trang web phức tạp.
Cách làm mới: Federated State (Event Bus & Local Stores)
Mỗi MFE sở hữu một Store riêng (Zustand, Redux, Valtio). Việc giao tiếp được thực hiện qua Custom Events hoặc URL.
Ưu điểm & Vấn đề giải quyết:
- Decoupling tuyệt đối: Các team tự do thay đổi cấu trúc dữ liệu nội bộ.
- Tính tự trị (Autonomy): Team Catalog có thể dùng Redux, trong khi Team Auth dùng Zustand, không ai làm phiền ai.
- Data Sharing: Chỉ chia sẻ những mảnh dữ liệu thực sự cần thiết (Vd: User Profile) thông qua một "Global Bridge".
2. Shared Data Client (React Query Synchronization)
Để tối ưu network, các MFE có thể dùng chung một QueryClientInstance.
Vấn đề giải quyết:
Nếu Team Catalog đã fetch dữ liệu người dùng tại /api/user, khi user chuyển sang trang Checkout, Team Checkout có thể lấy ngay dữ liệu từ cache mà không cần bắn thêm một request mạng nào nữa.
3. Đánh đổi (Trade-offs)
- Visual Inconsistency: Rủi ro lớn nhất là mỗi MFE trông như một trang web khác nhau. Cần đầu tư cực lớn vào Design Tokens.
- DX (Developer Experience): Việc debug lỗi giữa nhiều repo chạy runtime cùng lúc khó hơn nhiều so với Monolith. Cần các công cụ như Webpack Dashboard hoặc Chrome Extensions chuyên dụng.
4. Code minh họa: Giao tiếp bằng sự kiện (Event Bus)
// Phía Catalog MFE (Sender)
const addToCart = (item) => {
const event = new CustomEvent('shopee:cart-add', { detail: item });
window.dispatchEvent(event);
};
// Phía Header MFE (Receiver)
useEffect(() => {
const handler = (e) => {
updateBadge(e.detail);
};
window.addEventListener('shopee:cart-add', handler);
return () => window.removeEventListener('shopee:cart-add', handler);
}, []);Bài tập: Token Sharing
Hãy thực hiện thiết kế hệ thống style cho 2 remote:
- Remote A: Dùng Tailwind.
- Remote B: Dùng CSS Modules.
- Yêu cầu: Cả hai phải sử dụng chung một bộ biến màu sắc (
--primary,--secondary) định nghĩa tại Host App. Đảm bảo khi đổi màu tại Host, cả 2 Remote đều đổi màu theo mà không cần build lại. - Thử thách: Làm sao để CSS của Remote A (Tailwind) không ghi đè lên các style quan trọng của Remote B (CSS Modules)?
Câu hỏi phỏng vấn
Q: Tại sao "Shared Redux Store" lại là một Anti-pattern (mẫu thiết kế xấu) trong Microfrontends?
- Trả lời: Vì nó vi phạm nguyên tắc Isolation (Cô lập). Nếu 10 team cùng dùng chung 1 store, cấu trúc dữ liệu của team này sẽ dẫm chân lên team kia. Mỗi khi Team A thay đổi schema, Team B có thể bị crash code. Hơn nữa, nó tạo ra sự phụ thuộc chặt chẽ về mặt kĩ thuật (Tight coupling), khiến bạn không thể thay đổi thư viện State Management ở từng MFE độc lập. Giải pháp là mỗi MFE dùng Store riêng, và chỉ giao tiếp qua Events/URL.
Q: Làm sao để thực hiện Authentication (Xác thực) đồng bộ giữa các MFE?
- Trả lời: Chúng ta thực hiện Auth ở tầng Host/Shell. Sau khi user đăng nhập, Shell sẽ lưu JWT Token vào
LocalStorage/CookieshoặcGlobal State. Khi các MFE cần gọi API, chúng sẽ lấy token này từ Shell thông qua một "Auth Provider" hoặc một Shared Utility function. Cách này đảm bảo chỉ có một phiên đăng nhập duy nhất cho toàn bộ hệ thống.
Q: Làm sao để tránh rò rỉ CSS (Style leakage) giữa các Microfrontends?
- Trả lời: Có 3 cách chính:
- CSS-in-JS: Styled-components hoặc Emotion tự động tạo hash cho tên class, đảm bảo không trùng lặp.
- CSS Modules: Tương tự như CSS-in-JS, tạo tên class duy nhất.
- Tailwind + Prefix: Cấu hình Tailwind của từng MFE với một prefix riêng (Vd:
catalog-bg-red-500). - Shadow DOM: Cách ly hoàn toàn styles bên trong Web Components (tuy nhiên cách này khá phức tạp trong việc chia sẻ Design System).