Fab Forward Dev/

ナレッジベース

製造業の一般知識

イベント駆動アーキテクチャ / Event-Driven Architecture

CQRS、イベントソーシング、Saga パターン。 集約間の結果整合性とシステム間連携の基盤。

DDD 対応: ddd/04-domain-events.md, ddd/02-context-map.md (統合パターン)


1. ドメインイベント

定義

「ドメインで何かが起きた」ことを表す不変のオブジェクト

特徴説明
過去形OrderAccepted (受注受付された) — 過去に起きた事実
不変一度発行されたら変更不可
自己完結イベント自身に必要な情報をすべて含む

コマンド vs イベント

コマンドイベント
時制命令形 (CreateOrder)過去形 (OrderCreated)
意図「こうして欲しい」「こうなった」
受信者1 つ0 以上 (誰も聞いていなくてもよい)
失敗あり得る既に起きた事実 (失敗しない)

2. イベントストーミング (Event Storming)

目的

ドメインエキスパートと開発者が協働でドメインモデルを発見する手法。

プロセス

1. ドメインイベントを付箋で貼り出す (オレンジ)
   例: 「見積が承認された」「受注が登録された」

2. コマンドを追加 (青)
   例: 「見積を承認する」→ 「見積が承認された」

3. 集約を特定 (黄)
   例: コマンドとイベントをグルーピング → 「見積集約」

4. バウンデッドコンテキストを発見
   例: イベントの流れの断絶点 = コンテキスト境界

付箋の色

要素
オレンジドメインイベントOrderAccepted
コマンドAcceptOrder
集約SalesOrder
ピンク外部システム営業CRM
ポリシー/ルール「承認済み見積のみ受注可」
読取モデル受注一覧画面

3. CQRS (Command Query Responsibility Segregation)

原則

書き込み (Command) と読み取り (Query) を分離する

┌─ Command Side ─┐     ┌─ Query Side ─┐
│ CreateOrder     │     │ OrderList    │
│ → Domain Model  │────→│ → Read Model  │
│ → Write DB     │ イベント │ → Read DB    │
└────────────────┘     └──────────────┘

いつ使うか

状況CQRS が有効
読み書きの負荷が大きく異なる読取を最適化したい
複雑なドメインロジック書込み側に集中したい
複数集約の横断的な表示読取専用のビューを作りたい

我々のシステムでの適用

CostReport = CQRS の Read Model

Command Side: 受入登録, 日報登録, 検収登録 (各集約が独立に処理)
    ↓ ドメインイベント
Query Side: CostReport (材料費+労務費+検収額を非同期に集計)

4. イベントソーシング (Event Sourcing)

原則

状態を直接保存するのではなく、イベントの列として保存する

従来: orders テーブルの status = 'completed' を UPDATE
ES:  events テーブルに OrderAccepted, WorkStarted, WorkCompleted を INSERT
     → 現在の状態はイベントを再生 (replay) して導出

メリットとデメリット

メリットデメリット
完全な監査証跡複雑性の増加
時点指定での状態復元イベントスキーマの進化が難しい
イベント駆動が自然に実現読取パフォーマンス (→ CQRS で解決)

製造業での適用判断

ユースケースES が適する?理由
受注のステータス追跡ステータス遷移の履歴は有用だが、フル ES はオーバー
原価の積み上げ材料費・労務費の積み上げは本質的にイベントの列
マスタデータ変更頻度が低く、最新状態だけあればよい

推奨: フル ES ではなく、イベントの記録 + 状態テーブル のハイブリッド。


5. Saga パターン

問題

複数の集約/サービスにまたがるトランザクション。 分散トランザクション (2PC) は複雑で脆い。

解決: Saga

長期実行トランザクションを、ローカルトランザクションの連鎖 として実行する。 各ステップに 補償アクション (Compensating Action) を定義する。

コレオグラフィ vs オーケストレーション

コレオグラフィオーケストレーション
制御各サービスが自律的にイベントに反応中央のオーケストレーターが制御
結合度低いオーケストレーターに集中
可視性フロー全体が見えにくいフローが明確
適用シンプルなフロー (3-4 ステップ)複雑なフロー

製造業での Saga 例: 受注→手配→製作指示

コレオグラフィ方式:

1. SalesOrder: save() → OrderAccepted イベント発行
2. ProcurementService: OrderAccepted を受信 → PurchaseOrder 作成
3. ProductionService: OrderAccepted を受信 → ProductionInstruction 作成

補償: 受注キャンセル時
1. SalesOrder: cancel() → OrderCancelled イベント発行
2. ProcurementService: OrderCancelled を受信 → PurchaseOrder キャンセル
3. ProductionService: OrderCancelled を受信 → ProductionInstruction キャンセル

6. トランザクショナルアウトボックス

問題

「DB 更新とイベント発行を原子的に行う」のは難しい。

❌ 1. DB に save()  ← 成功
   2. イベント publish()  ← ここで失敗すると不整合

解決: アウトボックステーブル

✅ 1. DB に save() + outbox テーブルにイベントを INSERT (同一トランザクション)
   2. 別プロセスが outbox を polling → イベントを publish → outbox から削除

7. 設計判断のガイドライン

要件推奨パターン
集約内の整合性通常のトランザクション
集約間の整合性 (同一 BC)ドメインイベント + 結果整合性
BC 間の連携イベント駆動 (Webhook/メッセージキュー)
読取の最適化CQRS Read Model
完全な監査証跡イベント記録 (フル ES は不要なら避ける)
複数サービスのワークフローSaga (コレオグラフィ推奨)