Back to list
TypeScript で型安全なイベントバスを構築する:マイクロサービスを開発し、結合を解除する
Building a Type-Safe Event Bus in TypeScript: Decouple Your Microservices
Translated: 2026/3/21 7:00:12
Japanese Translation
TypeScript で型安全なイベントバスを構築する:マイクロサービスを開発し、結合を解除する
あなたの支払いサービスが通知に直接呼び出します。1 つの変更が 3 つのサービスを破滅させます。イベントバスは生産者から消費者への結合を解除します。型を持たないイベントは実行時に破綻します。タイポはコンパイルには問題ありませんが、本番環境でクラッシュを引き起こします。
interface EventMap {
"user.created": { id: string; email: string; name: string };
"user.deleted": { id: string };
"order.placed": { orderId: string; userId: string; total: number };
"payment.completed": { orderId: string; amount: number; currency: string };
}
type Handler = (payload: T) => void | Promise;
class EventBus {
private handlers = new Map();
on(event, handler) {
if (!this.handlers.has(event)) this.handlers.set(event, new Set());
this.handlers.get(event).add(handler);
return () => this.handlers.get(event).delete(handler);
}
async emit(event, payload) {
const h = this.handlers.get(event);
if (!h) return;
await Promise.all([...h].map(fn => Promise.resolve(fn(payload)).catch(console.error));
}
}
ローカルバスを Redis でラップしてクロスプロセスの型安全性を適用します:
class DistributedEventBus {
private local = new EventBus();
private pub: Redis;
private sub: Redis;
constructor(url: string) {
this.pub = new Redis(url);
this.sub = new Redis(url);
this.sub.on("message", (ch, msg) => this.local.emit(ch, JSON.parse(msg)));
}
on(event, handler) {
this.sub.subscribe(String(event));
return this.local.on(event, handler);
}
async emit(event, payload) {
await this.pub.publish(String(event), JSON.stringify(payload));
}
}
ハンドラーが失敗した際に、デッド LETTER キューにプッシュし、後で再試行します。EventBus を dlq アレイで拡張し、エラーをキャッチして retryDLQ() を提供します。モックは不要です。emit と assert。重複キーが同じレスポンスを返すか、unsubscribe がイベントを停止するか、並列処理が機能するかをテストします。はい:サービス間の結合解除、監査ログ、通知、分析。いいえ:リクエスト・レスポンスフロー。直接呼び出しや RPC を使用します。私の Production Backend Patterns シリーズの一部です。さらに実践的なバックエンドエンジニアリングを学ぶにはフォローしてください。この記事があなたに役立ったら、Ko-fi で私にコーヒーをください!さらに Production Backend Patterns の記事を読むにはフォローしてください。
Original Content
Building a Type-Safe Event Bus in TypeScript: Decouple Your Microservices Your payment service calls notification directly. One change breaks three services. An event bus decouples producers from consumers. Untyped events break at runtime. Typos compile fine but crash in production. interface EventMap { "user.created": { id: string; email: string; name: string }; "user.deleted": { id: string }; "order.placed": { orderId: string; userId: string; total: number }; "payment.completed": { orderId: string; amount: number; currency: string }; } type Handler = (payload: T) => void | Promise; class EventBus> { private handlers = new Map(); on(event, handler) { if (\!this.handlers.has(event)) this.handlers.set(event, new Set()); this.handlers.get(event).add(handler); return () => this.handlers.get(event).delete(handler); } async emit(event, payload) { const h = this.handlers.get(event); if (\!h) return; await Promise.all([...h].map(fn => Promise.resolve(fn(payload)).catch(console.error))); } } Wrap the local bus with Redis for cross-process type safety: class DistributedEventBus> { private local = new EventBus(); private pub: Redis; private sub: Redis; constructor(url: string) { this.pub = new Redis(url); this.sub = new Redis(url); this.sub.on("message", (ch, msg) => this.local.emit(ch, JSON.parse(msg))); } on(event, handler) { this.sub.subscribe(String(event)); return this.local.on(event, handler); } async emit(event, payload) { await this.pub.publish(String(event), JSON.stringify(payload)); } } When handlers fail, push to a dead letter queue and retry later. Extend EventBus with a dlq array that catches errors and provides retryDLQ(). No mocks needed. Emit and assert. Test duplicate key returns same response, unsubscribe stops events, concurrent handling works. Yes: Decoupling services, audit logs, notifications, analytics. No: Request-response flows. Use direct calls or RPC. Part of my Production Backend Patterns series. Follow for more practical backend engineering. If this article helped you, consider buying me a coffee on Ko-fi! Follow me for more production backend patterns.