はじめに
クロス MFE 通信は、Micro Frontend の最大の課題です。各 MFE は、共有メモリを使用せずに、独自のランタイムで実行されます。この記事では、安全な通信パターンを紹介します。
1. 黄金律
MFE は 疎結合である必要があります。共有状態ではなく、コントラクトを介して通信します。
❌ Shared Global State:
window.globalStore = { cart: { items: [...] } }
→ Coupling nightmare, race conditions
✅ Event-based Communication:
MFE A publishes: "cart:item-added" { productId, qty }
MFE B subscribes: update cart badge
→ Loosely coupled, observable
2. コミュニケーションパターン
2.1 カスタム イベント (ブラウザネイティブ)
// Product MFE - Publish
window.dispatchEvent(new CustomEvent('cart:item-added', {
detail: { productId: '123', name: 'Laptop', price: 999 }
}));
// Cart MFE - Subscribe
useEffect(() => {
const handler = (e) => {
setCartItems(prev => [...prev, e.detail]);
};
window.addEventListener('cart:item-added', handler);
return () => window.removeEventListener('cart:item-added', handler);
}, []);
2.2 イベントバス (カスタム)
class EventBus {
private handlers: Map<string, Set<Function>> = new Map();
on<T>(event: string, handler: (payload: T) => void) {
if (!this.handlers.has(event)) this.handlers.set(event, new Set());
this.handlers.get(event)!.add(handler);
return () => this.handlers.get(event)?.delete(handler);
}
emit<T>(event: string, payload: T) {
this.handlers.get(event)?.forEach(h => h(payload));
}
}
export const eventBus = new EventBus();
2.3 URL ベースの状態
/products?category=electronics&sort=price
→ URL là "shared state" tự nhiên: bookmarkable, shareable, no coupling
2.4 ブラウザのストレージ
// Shared preferences via localStorage
localStorage.setItem('user:preferences', JSON.stringify({
currency: 'VND', language: 'vi', theme: 'dark'
}));
3. 国家所有規則
| 状態 | オーナー | MFE がアクセスする方法 |
|---|---|---|
| 認証状態 | シェル | コンテキスト / イベント |
| カートのアイテム | カートMFE | イベント |
| 製品データ | 製品MFE | 共有されていません |
| ユーザー設定 | シェル | ローカルストレージ + イベント |
| URL パラメータ | シェル (ルーター) | URL |
| UI テーマ | シェル | CSS 変数 |
4. コントラクトファーストのコミュニケーション
// shared-types/events.ts
interface CartItemAddedEvent {
type: 'cart:item-added';
payload: { productId: string; name: string; price: number; quantity: number; };
}
interface CartUpdatedEvent {
type: 'cart:updated';
payload: { itemCount: number; total: number; };
}
5. ベストプラクティス
Mỗi MFE nên:
✅ Fetch dữ liệu riêng (own API calls)
✅ Manage state riêng (local state)
✅ Render independently
✅ Graceful degradation
Mỗi MFE KHÔNG nên:
❌ Import components từ MFE khác
❌ Access DOM của MFE khác
❌ Share runtime state qua window/global
❌ Assume MFE khác đã loaded
概要
- カスタム イベント — シンプル、ブラウザネイティブ
- イベント バス — 型付き、テスト可能
- URL — ナビゲーション状態に最適
- localStorage — ユーザー設定
- ルール: 各 MFE は独自の状態を管理、イベントを介して通信します