イベントソーシングパターン
このドキュメントでは、MBC CQRS Serverlessにおけるイベントソーシングの実装について説明します。
イベントソーシング概要
イベントライフサイクル
DynamoDBイベントストアスキーマ
キー構造
イベントストレージ用のDynamoDBキー構造:
- PK (Partition Key):
{TENANT}#{ENTITY_TYPE}(例:TENANT001#ORDER) - SK (Sort Key):
{ENTITY_TYPE}#{ID}@{version}(例:ORDER#20240101-001@1)
イベントレコード例
{
"pk": "TENANT001#ORDER",
"sk": "ORDER#20240101-001@1",
"version": 3,
"type": "OrderCreated",
"data": {
"orderId": "20240101-001",
"customerId": "CUST-001",
"items": [],
"totalAmount": 15000
},
"createdAt": "2024-01-01T10:00:00Z",
"createdBy": "user-123"
}
楽観的ロック
同時更新時のデータ整合性を確保するための楽観的ロックメカニズムについて説明します。
バージョン管理の実装
// コマンドサービスがバージョニングを自動的に処理
await this.commandService.publishAsync(entity, {
invokeContext: context,
});
// DynamoDBのConditionExpressionが楽観的ロックを確保
// ConditionExpression: 'attribute_not_exists(pk) AND attribute_not_exists(sk)'
// skにはバージョン番号が含まれており、同一バージョンのコマンドの重複書き込みを防ぎます
イベント処理パイプライン
イベントハンドラーの実装
import { EventHandler, IEventHandler } from '@mbc-cqrs-serverless/core';
@EventHandler(OrderCreatedEvent)
export class OrderCreatedHandler implements IEventHandler<OrderCreatedEvent> {
constructor(
private readonly notificationService: NotificationService,
private readonly readModelService: ReadModelService,
) {}
async execute(event: OrderCreatedEvent): Promise<void> {
// リードモデルを更新
await this.readModelService.updateOrderSummary(event);
// 通知を送信
await this.notificationService.sendOrderConfirmation(event);
}
}
イベントソーシングの利点
イベントソーシングを採用することで以下の利点が得られます:
- 完全な監査証跡: すべての状態変更がイベントとして記録される
- タイムトラベル: 任意の時点での状態を再構築可能
- イベントリプレイ: プロジェクションを再構築するためにイベントをリプレイ
- デバッグ: 操作の正確なシーケンスを追跡
- 分析: ビジネスインテリジェンス用の豊富なイベントデータ
- 統合: イベントが外部システムの更新をトリガー可能
ベストプラクティス
効果的なイベントソーシングのためのベストプラクティス:
- イミュータブルイベント: 保存されたイベントを変更しない
- べき等ハンドラー: 重複イベント配信を適切に処理
- イベントバージョニング: イベントスキーマの進化を計画
- 相関ID: サービス間で関連イベントを追跡
- デッドレターキュー: 失敗したイベント処理を処理
関連ドキュメント
- CQRSフロー - コマンドとクエリの分離
- コマンドサービス - コマンドを使ったイベント発行
- DynamoDB - イベントストアの実装
- バージョン競合ガイド - イベントを使った楽観的ロック