ドメインイベント
アプリケーションが成長すると、1つの処理にいろいろな後続処理がくっついてきます。ログイン処理なのに統計更新、メール通知、監査ログ記録まで全部知っている...。これを解決するのが「ドメインイベント」です。
「何が起きたか」を表すオブジェクト
ドメインイベントとは 、システム内で発生したビジネス上意味のある出来事を表すオブジェクトです。
┌─────────────────────────────────────────────────────────────┐
│ ドメインイベントの例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 認証システム: │
│ ├── UserRegistered(userId, email, timestamp) │
│ ├── LoginSucceeded(userId, ipAddress, timestamp) │
│ ├── LoginFailed(userId, reason, timestamp) │
│ ├── PasswordChanged(userId, timestamp) │
│ └── TokenIssued(userId, clientId, scopes, timestamp) │
│ │
│ ECサイト: │
│ ├── OrderPlaced(orderId, items, total, timestamp) │
│ ├── PaymentCompleted(orderId, amount, timestamp) │
│ └── OrderShipped(orderId, trackingNumber, timestamp) │
│ │
│ 共通する特徴: │
│ ・過去形(〜した) │
│ ・変更不可(起きた事実は変えられない) │
│ ・いつ起きたか(タイムスタンプ)を持つ │
│ │
└─────────────────────────────────────────────────────────────┘
なぜイベントとして表現するのか
Before: 状態の直接変更
┌─────────────────────────────────────────────────────────────┐
│ ログイン処理 │
│ │
│ 1. パスワード検証 │
│ 2. セッション作成 │
│ 3. ログインカウント +1 ← DB UPDATE │
│ 4. 最終ログイン時刻更新 ← DB UPDATE │
│ 5. 統計テーブル更新 ← DB UPDATE │
│ 6. メール通知送信 ← HTTP 通信 │
│ 7. Slack通知 ← HTTP 通信 │
│ 8. 監査ログ記録 ← DB INSERT │
│ │
│ → ログイン処理が通知・統計・監査の詳細を全部知っている │
│ → 新しい通知先が増えるたびにログイン処理を修正 │
│ → 処理が長くなる、テストしにくい、障害が波及する │
└─────────────────────────────────────────────────────────────┘
After: イベントの発行
┌─────────────────────────────────────────────────────────────┐
│ ログイン処理 │
│ │
│ 1. パスワード検証 │
│ 2. セッション作成 │
│ 3. イベント発行: LoginSucceeded(userId, timestamp) │
│ → 完了。ログイン処理はここまで │
│ │
│ イベントの購読者(独立して動作): │
│ ├── 統計更新サービス → 統計テーブル更新 │
│ ├── 通知サービス → メール/Slack 送信 │
│ ├── 監査ログサービス → 監査ログ記録 │
│ └── 分析サービス → ClickHouse に送信 │
│ │
│ → ログイン処理は通知・統計の存在を知らない │
│ → 新しい購読者を追加してもログイン処理は変更不要 │
│ → 各サービスが独立してテスト・デプロイ可能 │
└─────────────────────────────────────────────────────────────┘
このパターンは Pub/Sub(Publish/Subscribe) として広く知られています。ドメインイベントは DDD の用語ですが、仕組みは Pub/Sub そのものです。
| 文脈 | 発行側 | 仲介 | 購読側 |
|---|---|---|---|
| デザインパターン | Publisher | Event Channel | Subscriber |
| DDD | ドメインイベント発行 | Event Bus | イベントハンドラ |
| Spring | ApplicationEventPublisher | Spring Event | @EventListener |
| Kafka | Producer | Topic | Consumer |
| AWS | Publisher | SNS / EventBridge | Subscriber / Lambda |
名前は違いますが、本質は同じ ── 発行者は購読者を知らない、購読者は好きなイベントを購読する。
イベントの3つの性質
1. 不変(Immutable)
起きた事実は変えられません。「ログインした」は取り消せません。
❌ event.setTimestamp(newTime) // イベントは変更できない
✅ 新しいイベントを発行する // 「パスワードリセットした」
2. 過去形(Past Tense)
イベントは「起きたこと」なので、命名は過去形。
❌ Login, CreateUser, SendEmail // コマンド(指示)
✅ LoginSucceeded, UserCreated, EmailSent // イベント(事実)
3. 自己完結(Self-contained)
イベントを見るだけで何が起きたかわかる。他のデータを参照する必要がない。
❌ LoginEvent(userId: 123)
→ 何が起きた? 成功? 失敗? いつ?
✅ LoginSucceeded(
userId: 123,
tenantId: "tenant-A",
ipAddress: "1.2.3.4",
timestamp: "2026-03-25T09:00:00Z"
)
→ これだけで完全にわかる
イベントの活用パターン
パターン1: 通知(Fire and Forget)
イベント発行 ──→ 購読者が非同期で処理
├── メール送信
├── Slack通知
└── Webhook呼び出し
特徴: 発行者は結果を気にしない
用途: 通知、ログ記録
パターン2: 状態更新(Event-Carried State Transfer)
イベント発行 ──→ 購読者が自分のデータストアを更新
├── 統計テーブル更新
├── 検索インデックス更新
└── キャッシュ更新
特徴: 各サービスが自分用のデータコピーを持つ
用途: CQRS の読み取りモデル構築
パターン3: イベント記録(Event Sourcing)
イベント発行 ──→ イベントストアに永続化
→ 現在の状態はイベントの再生で導出
特徴: イベント自体がデータの本体
用途: 監査証跡、時間旅行、デバッグ
→ 次のドキュメントで詳しく解説