Files
2nd/10_Wiki/Topics/Architecture/Event Mediator.md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
10_Wiki/Topics 대규모 정리:
- 오류 캡처/미완성 stub 문서 227개 제거
- 교차폴더 중복 43클러스터 병합 (63파일 → redirect)
- 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건
- 카테고리 MOC 6개 신규 생성
- Graph 섹션 미해결 related-keyword 링크 10,058건 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:52:15 +09:00

197 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wiki-2026-0508-event-mediator
title: Event Mediator
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Mediator Pattern, Event Bus]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [architecture, design-pattern, event-driven, decoupling]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: typescript
framework: nodejs
---
# Event Mediator
## 매 한 줄
> **"매 N×N component coupling 의 N×1 mediator hub 의 collapse"**. Event Mediator 매 multiple components 의 direct coupling 의 elimination — 매 components 매 mediator 를 publish/subscribe, 매 mediator 매 routing/orchestration 의 own. GoF Mediator pattern 의 event-driven evolution.
## 매 핵심
### 매 Mediator vs Observer
- **Observer**: 매 1 subject → N observers, 매 unidirectional broadcast.
- **Mediator**: 매 N senders ↔ N receivers, 매 hub 의 bidirectional routing.
- **Event Bus**: 매 Mediator 의 generic implementation — 매 string-keyed event types.
### 매 동기 vs 비동기
- **Sync mediator**: 매 in-process method dispatch — 매 EventEmitter, MediatR.
- **Async mediator**: 매 message queue 의 backed — 매 RabbitMQ, Kafka, Redis Streams.
- **Hybrid**: 매 in-process sync + cross-service async (e.g., NestJS CQRS).
### 매 응용
1. UI components decoupling — chat rooms, form fields.
2. Microservices orchestration — saga coordinator.
3. CQRS command/query bus — MediatR.
4. Game event systems — unit death, achievement triggers.
## 💻 패턴
### Typed Event Bus (TypeScript)
```typescript
type EventMap = {
'user.signup': { userId: string; email: string };
'order.placed': { orderId: string; total: number };
'cache.invalidate': { key: string };
};
class EventMediator<M> {
private handlers = new Map<keyof M, Set<(p: any) => void | Promise<void>>>();
on<K extends keyof M>(event: K, handler: (payload: M[K]) => void | Promise<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);
}
async emit<K extends keyof M>(event: K, payload: M[K]) {
const hs = this.handlers.get(event);
if (!hs) return;
await Promise.all([...hs].map(h => h(payload)));
}
}
const bus = new EventMediator<EventMap>();
bus.on('user.signup', async ({ userId, email }) => {
await sendWelcomeEmail(email);
});
```
### Mediator with Request/Response (MediatR-style)
```typescript
interface IRequest<TResponse> { __response?: TResponse }
interface IHandler<TReq extends IRequest<TRes>, TRes> {
handle(req: TReq): Promise<TRes>;
}
class Mediator {
private handlers = new Map<Function, IHandler<any, any>>();
register<T extends IRequest<R>, R>(reqCtor: new (...a: any[]) => T, h: IHandler<T, R>) {
this.handlers.set(reqCtor, h);
}
async send<R>(req: IRequest<R>): Promise<R> {
const h = this.handlers.get(req.constructor);
if (!h) throw new Error(`No handler for ${req.constructor.name}`);
return h.handle(req);
}
}
class GetUserQuery implements IRequest<User> { constructor(public id: string) {} }
class GetUserHandler implements IHandler<GetUserQuery, User> {
async handle(q: GetUserQuery) { return db.users.findById(q.id); }
}
```
### Saga Mediator (orchestrated)
```typescript
class OrderSaga {
constructor(private bus: EventMediator<OrderEvents>) {
bus.on('order.created', this.onCreated.bind(this));
bus.on('payment.failed', this.onPaymentFailed.bind(this));
}
async onCreated({ orderId }: { orderId: string }) {
await this.bus.emit('payment.requested', { orderId });
}
async onPaymentFailed({ orderId, reason }: any) {
await this.bus.emit('order.cancelled', { orderId, reason });
await this.bus.emit('inventory.released', { orderId });
}
}
```
### Middleware Pipeline
```typescript
type Middleware<E> = (event: E, next: () => Promise<void>) => Promise<void>;
class PipelineMediator<E> {
private middlewares: Middleware<E>[] = [];
use(m: Middleware<E>) { this.middlewares.push(m); }
async dispatch(event: E) {
let i = -1;
const run = async (idx: number): Promise<void> => {
if (idx <= i) throw new Error('next() called twice');
i = idx;
const fn = this.middlewares[idx];
if (fn) await fn(event, () => run(idx + 1));
};
await run(0);
}
}
// usage: logging, validation, retry, dead-letter
```
### Cross-Service Mediator (Kafka)
```typescript
import { Kafka } from 'kafkajs';
const kafka = new Kafka({ brokers: ['localhost:9092'] });
const producer = kafka.producer();
const consumer = kafka.consumer({ groupId: 'order-svc' });
await consumer.subscribe({ topic: 'orders', fromBeginning: false });
await consumer.run({
eachMessage: async ({ message }) => {
const evt = JSON.parse(message.value!.toString());
await handler[evt.type]?.(evt.payload);
},
});
await producer.send({
topic: 'orders',
messages: [{ key: orderId, value: JSON.stringify({ type: 'order.placed', payload }) }],
});
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| 2-3 components 의 direct call | 매 mediator skip — 매 over-engineering |
| 5+ components, fan-out broadcast | 매 in-process EventEmitter |
| CQRS / clean architecture | 매 MediatR-style request bus |
| Cross-service, durability | 매 Kafka / RabbitMQ |
| Long-running workflow | 매 saga orchestrator (Temporal, Inngest) |
**기본값**: 매 typed in-process EventEmitter (Node) 또는 MediatR (.NET) — 매 cross-service 의 Kafka.
## 🔗 Graph
- 부모: [[Design-Patterns]] · [[Event-Driven-Architecture]]
- 변형: [[Observer-Pattern]] · [[Pub-Sub]]
- 응용: [[CQRS]] · [[Microservices]]
- Adjacent: [[Event-Sourcing]]
## 🤖 LLM 활용
**언제**: 매 N×N coupling 의 emerge — 매 5+ components 매 mutual callbacks. CQRS / saga implementation.
**언제 X**: 매 simple 2-component dependency — 매 direct injection 매 sufficient. Hot-path latency-critical (mediator dispatch overhead).
## ❌ 안티패턴
- **God Mediator**: 매 mediator 매 business logic 의 absorb — 매 anemic handlers, 매 mediator 의 1000+ lines. Mediator 의 routing only.
- **Event soup**: 매 untyped string events — 매 'user_signup' vs 'userSignup' typo 의 silent failure. Typed map 의 use.
- **Sync chain pretending async**: 매 emit() 매 await chain 의 200ms latency — 매 async queue 의 use.
- **Lost events**: 매 in-memory bus 매 crash 의 lose — 매 durability 매 required 의 persistent queue.
## 🧪 검증 / 중복
- Verified (GoF Design Patterns; MediatR docs; NestJS CQRS).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Event Mediator pattern 의 typed/saga/Kafka 의 5 patterns |