DCB とは何か ── 「一貫性は構造ではなく条件」

DCB のコアコンセプト
**Dynamic Consistency Boundary(DCB)**を一言で表すなら:
一貫性は構造ではなく条件になる(Consistency becomes a condition, not a structure)
集約は「このオブジェクトの塊がトランザクションの境界です」という構造的な定義だ。DCB は「このイベントクエリの結果が前提条件を満たしている限り書き込みを許可する」という条件的な定義だ。
集約(静的):
「Course ストリームの中で 30名の上限を守る」
→ 境界 = Course ストリーム = 設計時に固定
DCB(動的):
「タグ course:C1 を持つ registered イベントが 30件未満であることを確認する」
→ 境界 = そのクエリが返すイベント集合 = 実行時に決定
3つの構成要素
DCB は以下の3ステップで動作する。
1. イベントのクエリ(Decision Model の構築)
特定の判断に必要なイベントだけを、タグとイベントタイプで動的に選択する。
「受講者 S1 が講座 C1 に登録できるか?」を判断するために必要なイベント:
クエリ1: タグ "course:C1" を持つ "student-registered" イベント → 何件か?
クエリ2: タグ "student:S1" を持つ "student-registered" イベント → 何件か?
これにより、Course 集約と Student 集約という分離された構造を通さずに、判断に必要な情報だけを直接取得できる。
2. ビジネスロジックの適用
クエリ結果に基づいてドメインルールを検証する。
def can_register(course_registrations: int, student_registrations: int) -> bool:
return course_registrations < 30 and student_registrations < 10
3. 前提条件付き書き込み(Conditional Append)
「クエリした時点」と「書き込む時点」の間に、条件を覆すイベントが追加されていないことを保証する。
1. クエリ実行 → "course:C1 の登録数 = 29" を確認(最終イベント位置 = P100)
2. ビジネスルール → 30未満なので OK
3. 書き込み → 「位置 P100 以降に、タグ "course:C1" の "student-registered" が
追加されていなければ書き込む」
4. → もし P100 以降に別のリクエストで登録が入っていたら書き込みが拒否される
→ クライアントはリトライする(楽観的ロック)
集約 vs DCB の対比
graph TD
subgraph "集約ベース(従来)"
CS[Course ストリーム] -->|登録イベント| CS
SS[Student ストリーム] -->|登録イベント| SS
Saga[Saga が 2つのストリームを調整]
CS -.-> Saga
SS -.-> Saga
end
subgraph "DCB ベース"
ES[統一イベントストア] -->|タグで動的フィルタ| Q1[course:C1 のイベント]
ES -->|タグで動的フィルタ| Q2[student:S1 のイベント]
Q1 & Q2 --> DM[Decision Model<br/>両方の条件を同時に検証]
DM -->|条件付き書き込み| ES
end
| 観点 | 集約 | DCB |
|---|---|---|
| 境界の決定時期 | 設計時(静的) | 実行時(動的) |
| イベントの格納 | 集約ごとのストリームに分離 | 1つのストリーム(タグで分類) |
| クロス集約の制約 | Saga / 結果整合性 | 単一クエリで両方を検証 |
| 並行制御 | ストリーム単位の楽観的ロック | クエリ結果に基づく条件付きロック |
| リファクタリング | ストリーム構造の変更が困難 | タグとクエリの変更で柔軟に対応 |
「タグ」という革新
集約ベースのイベントソーシングでは、イベントは1つのストリームに属する。
従来:
Stream "course-C1": [StudentRegistered, StudentRegistered, ...]
Stream "student-S1": [CourseRegistered, CourseRegistered, ...]
→ 「受講者 S1 が講座 C1 に登録した」事実を
どちらのストリームに入れるか? → 片方に入れてもう片方はイベント複製?
DCB では、1つのイベントに複数のタグを付ける。
DCB:
Event: StudentRegisteredToCourse
tags: ["course:C1", "student:S1"]
data: { student_id: "S1", course_id: "C1", timestamp: "..." }
→ 1つのイベントが「講座 C1 のイベント」でもあり「受講者 S1 のイベント」でもある
→ タグでフィルタすればどちらの視点でも検索できる
→ イベントの重複が不要
Sara Pellegrini の言葉を借りれば:
タグにより、イベントの履歴を複数の次元でスライスできる。
DCB がもたらす変化
Before DCB:
「このドメインモデルの集約境界はどこにすべきか?」
→ 設計時に最適な境界を見極める必要がある
→ 間違えると後から変更が困難
→ ストリーム構造がコードとインフラに固定される
After DCB:
「この操作にはどのイベントが必要で、どの条件を満たすべきか?」
→ 操作ごとに必要な一貫性を定義
→ 設計の変更はクエリとタグの変更で済む
→ イベントストアの構造は変わらない
次章では、DCB の技術的な仕組み──タグ、クエリ、楽観的ロック──の詳細を見ていく。