Làm thế nào để hệ thống MFE của bạn vẫn nhanh như chớp và không bao giờ "chết" hoàn toàn khi một phần server bị sập?
1. Phân tích thiết kế: Khả năng tự phục hồi (Resilience)
Cách làm truyền thống: Single Point of Failure
Trong Monolith, nếu một đoạn code JS gặp lỗi runtime nghiêm trọng, toàn bộ ứng dụng sẽ bị crash trắng trang.
Nhược điểm:
- Lỗi dây chuyền: Một lỗi nhỏ ở phần "Gợi ý sản phẩm" có thể khiến người dùng không thể nhấn nút "Thanh toán".
- Trải nghiệm người dùng kém: Người dùng không có cách nào tương tác tiếp với các phần còn lại của trang web.
Cách làm mới: Error Boundaries & Fallbacks
Mỗi MFE được bao bọc trong một "lồng bảo vệ" (Error Boundary). Nếu một Remote bị lỗi, Shell App sẽ tự động hiển thị một giao diện thay thế nhẹ nhàng.
Ưu điểm & Vấn đề giải quyết:
- Graceful Degradation: Trang web vẫn chạy được các phần quan trọng (Checkout, Auth) ngay cả khi phần phụ (Live Stream, Ads) bị sập.
- Isolate Failure: Lỗi được cô lập chặt chẽ trong phạm vi của Remote đó.
2. Prefetching Remotes
Để giải quyết độ trễ mạng khi tải các file remoteEntry.js, chúng ta sử dụng chiến lược Predictive Loading.
Kỹ thuật:
- Tải MFE ngay khi người dùng hover vào menu.
- Hoặc dùng
Service Workersđể cache trước các Remote quan trọng trong thời gian máy tính đang rảnh (Idle time).
3. Đánh đổi (Trade-offs)
- Dư thừa tài nguyên: Việc tải trước (Prefetch) có thể làm tốn băng thông của người dùng nếu họ không thực sự vào trang đó.
- Phức tạp trong Monitoring: Cần hệ thống tracking cực kỳ chi tiết để biết chính xác MFE nào đang gây chậm (TTI) cho người dùng.
4. Code minh họa: Error Boundary & Fallback
// Shell App
import { ErrorBoundary } from 'react-error-boundary';
const RemoteCatalog = React.lazy(() => import('catalog/App'));
const CatalogSection = () => (
<ErrorBoundary
fallback={<div className="error-card">Tính năng Catalog tạm thời bảo trì</div>}
onError={(error) => console.error("MFE Catalog Error:", error)}
>
<Suspense fallback={<Skeleton />}>
<RemoteCatalog />
</Suspense>
</ErrorBoundary>
);Bài tập: Simulate Down
Thực hiện các bước sau để test tính ổn định:
- Khởi chạy App Shell và Remote Catalog.
- Tắt server của Remote Catalog (giết process).
- Yêu cầu: App Shell vẫn phải hiển thị Header/Footer và hiển thị một thông báo "Section Catalog đang bảo trì" (Fallback UI) thay vì crash cả trang web.
- Nâng cao: Thêm cơ chế Retry tự động (Vd: cứ 5 giây thử tải lại Remote Catalog một lần).
Câu hỏi phỏng vấn
Q: Làm sao để tối ưu hóa chỉ số LCP (Largest Contentful Paint) trong Microfrontends?
- Trả lời: LCP thường bị ảnh hưởng bởi việc phải tải nhiều file JS trước khi render.
- SSR: Thực hiện render HTML tại server (Server-side Federation).
- Prefetch: Tải trước file
remoteEntry.jscủa các Remote quan trọng ngay sau khi Main app khởi chạy. - Critical Assets: Đảm bảo các ảnh LCP (Vd: banner trang chủ) không nằm trong một Remote được tải quá muộn.
- Deduplication: Dùng Module Federation để đảm bảo không tải lại các thư viện lớn như React/Lodash nhiều lần.
Q: Điều gì xảy ra nếu một Remote MFE mất 10 giây mới phản hồi?
- Trả lời: Đây là kịch bản tồi tệ. Chúng ta phải áp dụng Timouts và Fallbacks.
- Sử dụng
Promise.raceđể giới hạn thời gian tải Remote (Vd: sau 2s không có code thì báo lỗi). - Sử dụng
ErrorBoundaryđể hiển thị một giao diện thay thế (Offline mode). - Tránh việc các Remote "chặn" (blocking) việc hiển thị của App Shell.
- Sử dụng
Q: Bạn đo lường hiệu năng của từng Microfrontend như thế nào?
- Trả lời: Chúng ta không thể chỉ nhìn vào Google Lighthouse của trang tổng. Chúng ta cần đo:
- Asset waterfall: Thời gian tải của từng
remoteEntry.js. - Hydration time: Thời gian React tiêu tốn để khởi tạo từng component từ CDN.
- Custom Metrics: Dùng
Performance.mark()vàPerformance.measure()gắn vào từng MFE để gửi dữ liệu về hệ thống giám sát (Vd: Datadog, Grafana). flagship.
- Asset waterfall: Thời gian tải của từng