はじめに
イベント駆動型アーキテクチャでは、サービスが互いに直接呼び出す (結合) のではなく、イベント を介してサービスが通信できるため、結合が減少し、スケーラビリティが向上し、複雑なシステムの構築が容易になります。
1. イベント駆動型アーキテクチャ (EDA) とは何ですか?
1.1 リクエスト駆動型とイベント駆動型
Request-Driven (Coupling cao):
Order Service ──POST──► Inventory Service
Order Service ──POST──► Payment Service
Order Service ──POST──► Email Service
Order Service phải biết TẤT CẢ downstream services
Thêm service mới → Sửa Order Service
Event-Driven (Loose coupling):
Order Service ──publish──► "OrderCreated" Event
│
┌───────────┼───────────┐
▼ ▼ ▼
Inventory Payment Email
Service Service Service
Order Service KHÔNG biết ai subscribe
Thêm service mới → Subscribe event, KHÔNG sửa gì
1.2 イベントの種類
1. Event Notification (thin):
{ "type": "OrderCreated", "orderId": "123" }
→ Consumer phải query lại để lấy details
2. Event-Carried State Transfer (fat):
{ "type": "OrderCreated",
"orderId": "123",
"userId": "456",
"items": [...],
"total": 1500000 }
→ Consumer có đủ data, không cần query lại
3. Domain Event:
{ "type": "OrderCreated",
"aggregateId": "order-123",
"aggregateType": "Order",
"version": 1,
"timestamp": "2024-01-15T10:30:00Z",
"data": { ... } }
→ Dùng trong DDD, có aggregate context
2. イベントソーシング
2.1 従来の CRUD とイベント ソーシング
CRUD:
State: { balance: 700 }
Chỉ biết balance hiện tại = 700
KHÔNG biết lịch sử thay đổi
Event Sourcing:
Events:
1. AccountCreated { balance: 1000 }
2. MoneyWithdrawn { amount: 200 }
3. MoneyDeposited { amount: 500 }
4. MoneyWithdrawn { amount: 600 }
Current state = replay events:
1000 - 200 + 500 - 600 = 700
Biết TOÀN BỘ lịch sử
Có thể rebuild state tại bất kỳ thời điểm
Audit trail hoàn chỉnh
2.2 イベントストア
┌────────────────────────────────────────────────────┐
│ Event Store │
├──────┬──────────┬────────┬──────────────┬──────────┤
│ SeqNo│ AggregateId│ Type │ Data │ Timestamp│
├──────┼──────────┼────────┼──────────────┼──────────┤
│ 1 │ acct-001 │ Created│ {balance:1000}│ 10:00:00│
│ 2 │ acct-001 │ Withdraw│{amount: 200} │ 10:05:00│
│ 3 │ acct-002 │ Created│ {balance:500} │ 10:06:00│
│ 4 │ acct-001 │ Deposit│ {amount: 500} │ 10:10:00│
│ 5 │ acct-001 │ Withdraw│{amount: 600} │ 10:15:00│
└──────┴──────────┴────────┴──────────────┴──────────┘
Immutable! Không UPDATE, không DELETE
Chỉ APPEND events mới
2.3 スナップショット
Vấn đề: Account có 1 triệu events → replay chậm
Giải pháp: Snapshot (checkpoint)
Events 1-999,999: (lịch sử cũ)
Snapshot @ event 999,999: { balance: 52,345 }
Events 1,000,000-1,000,005: (events mới)
Rebuild state:
Load snapshot: 52,345
Replay 5 events (thay vì 1 triệu!)
3.CQRS
3.1 コマンドクエリの責任の分離
Traditional (1 model cho cả Read và Write):
┌────────────┐
│ Model │ ← cả Read và Write
│ (Order) │ dùng chung schema
└─────┬──────┘
│
┌─────▼──────┐
│ Database │
└────────────┘
CQRS (tách Read và Write model):
Write (Command) Read (Query)
┌────────────┐ ┌────────────┐
│ Command │ │ Query │
│ Model │ │ Model │
│ (normalize)│ │ (denormalize)│
└─────┬──────┘ └─────┬──────┘
│ │
┌─────▼──────┐ ──event──►┌───▼────────┐
│Write DB │ │ Read DB │
│(PostgreSQL)│ │(Elastic/ │
│ ACID │ │ Redis/Mongo)│
└────────────┘ └────────────┘
3.2 CQRS + イベントソーシング
Command Side:
User: "Đặt hàng"
→ Command: CreateOrder
→ Validate business rules
→ Append event: OrderCreated
→ Event Store (source of truth)
Event Side:
OrderCreated event published
│
├──► Read Model Projector
│ → Update denormalized view (Read DB)
│
├──► Inventory Service
│ → Reserve items
│
└──► Notification Service
→ Send confirmation
Query Side:
User: "Xem đơn hàng"
→ Query Read DB (optimized for reads)
→ Return instantly (pre-computed view)
4. サーガパターン
4.1 分散トランザクションの問題
Đặt hàng cần 3 bước (3 services khác nhau):
1. Payment Service: Charge credit card
2. Inventory Service: Reserve items
3. Shipping Service: Create shipment
Nếu bước 3 fail → Phải rollback bước 1, 2
Không thể dùng database transaction (khác databases!)
4.2 振付サーガ
Không có orchestrator, services tự coordinate qua events
Order ──OrderCreated──► Payment
│
PaymentCharged
│
▼
Inventory
│
ItemsReserved
│
▼
Shipping
│
ShipmentCreated
│
▼
Order: COMPLETED
Rollback (nếu Shipping fail):
Shipping ──ShipmentFailed──► Inventory
│
ItemsReleased
│
▼
Payment
│
PaymentRefunded
│
▼
Order: CANCELLED
4.3 オーケストレーションの物語
Orchestrator điều phối tất cả steps
┌──────────────┐
│ Saga │
│ Orchestrator │
└──────┬───────┘
│
┌────▼────┐ Success ┌─────────┐ Success ┌──────────┐
│Payment │──────────►│Inventory│──────────►│Shipping │
│Service │ │Service │ │Service │
└─────────┘ └─────────┘ └──────────┘
│ │ │
Compensate Compensate Compensate
(Refund) (Release) (Cancel)
Ưu điểm: Logic tập trung, dễ debug
Nhược điểm: Orchestrator = potential SPOF
5. イベントスキーマの進化
Vấn đề: Event schema thay đổi theo thời gian
v1: { "orderId": "123", "amount": 100 }
v2: { "orderId": "123", "amount": 100, "currency": "VND" }
v3: { "orderId": "123", "total": { "amount": 100, "currency": "VND" } }
Strategies:
1. Schema Registry (Confluent/Avro):
Quản lý versions, validate compatibility
2. Upcasting:
Khi đọc event cũ → Transform sang schema mới
v1 event → upcaster → v3 format
3. Backward/Forward compatibility:
- Thêm field mới: có default value
- Không rename/remove fields
- Consumers ignore unknown fields
概要
| パターン | 使用例 | 複雑さ |
|---|---|---|
| イベントのお知らせ | 疎結合 | 低い |
| イベントソーシング | 監査、一時的なクエリ | 高 |
| CQRS | 読み取り/書き込みの最適化 | 中~高 |
| 振付サーガ | シンプルなワークフロー、少ない手順 | 中 |
| オーケストレーションサーガ | 複雑なワークフロー、多くの手順 | 高 |
演習
-
イベント フロー: 電子商取引: ユーザーが注文 → 在庫の確認 → 支払いの請求 → メールの送信 → 分析の更新。イベントフロー図を描きます。どのサービスがどのイベントを公開しますか?どのイベントに登録しますか?
-
Saga Design: 航空券の予約: 航空券の予約→ホテルの予約→車の予約。予備車が故障した場合の補償フローを設計します。コレオグラフィーまたはオーケストレーションを使用しますか?
-
CQRS: 「ベストセラー製品」機能の CQRS を設計します: 書き込みモデル (注文) と読み取りモデル (キャッシュされたランキング)。どのイベントが読み取りモデルの更新をトリガーしますか?