🤖 AI 駆動開発で CQRS が必要な理由
AI コーディングツールに「この管理画面を作って」と指示すると、 画面表示の都合でドメインモデルを捻じ曲げる コードが出てくる。JOIN を 5 段重ねたクエリ、ドメインに不要な表示項目、計算済みカラムの追加—ドメインロジックが画面の影響を受け始めたら危険信号。
CQRS(Command Query Responsibility Segregation)は、 書き込みと読み取りを別モデルにする ことでこの問題に構造的な解を与える。AI 駆動開発での効用は 3 つ。
| AI 駆動開発での効用 | CQRS がもたらす状態 |
|---|---|
| ドメインモデルを AI から守る | 書き込みは純粋な集約、読み取りは別モデル |
| 画面要件に AI が自由に応えられる | Read Model を画面ごとに作らせても問題なし |
| スケーリング戦略を分離できる | 読み取り側だけをキャッシュ・レプリカ化 |
本記事は開発手法ガイド:概要のシリーズ 7 本目。Greg Young が提唱した CQRS を、AI 駆動開発の文脈で整理します。
📜 CQRS の起源と本質
CQS から CQRS へ
Bertrand Meyer が提唱した CQS(Command Query Separation) は「メソッドは状態を変更する Command か、状態を返す Query のどちらかであるべき」という原則。Greg Young が 2010 年頃、これを アーキテクチャレベル に拡張したのが CQRS。
| CQS | CQRS |
|---|---|
| メソッド単位で分離 | モデル・経路・場合によっては DB まで分離 |
| 1 つのオブジェクト内で完結 | 書き込みと読み取りで別のオブジェクトツリー |
なぜモデルごと分けるのか
通常の CRUD では、同じドメインモデル(例: Order クラス)が 書き込みに必要な不変条件 と 読み取りに必要な集約・整形 の両方を抱える。これが肥大化の原因。
書き込み側で必要なもの:
- 不変条件の検証(決済済みの注文はキャンセルできない等)
- 集約境界内でのトランザクション整合性
読み取り側で必要なもの:
- 画面用の集計(過去 30 日の注文合計)
- 多テーブル JOIN された表示用データ
- モール別の在庫状態の一覧
これを 1 つのモデルに詰め込むと、画面追加のたびにドメインモデルが汚染される。CQRS は 最初から 2 つに分ける 。
🏗️ CQRS の構造
Client
├─ Command(書き込み) → 集約 → 書き込み DB
│ ↓
│ イベント発行
│ ↓
└─ Query(読み取り) ← Read Model(参照用) ← イベント反映
| 要素 | 役割 |
|---|---|
| Command | 「◯◯せよ」という意図。書き込み側のみが受け付ける |
| 集約 | Command を適用するドメインモデル(書き込み側) |
| イベント | 集約の変更を表す事実。Read Model 更新のトリガー |
| Read Model | 画面・検索・レポート用に最適化された参照ビュー |
| Query | 「◯◯を取得せよ」という意図。Read Model のみを触る |
📖 Read Model の設計
Read Model は、 「その画面・その検索・そのレポートに必要な形」で作る 。正規化を気にせず、冗長も許容する。
Read Model の 3 つの実装パターン
| パターン | 実装方法 | 向いている場面 |
|---|---|---|
| 別テーブル化 | 書き込み DB の中に Read Model 用テーブルを作る | 単一 DB、小〜中規模 |
| Materialized View | DB の Materialized View 機能 | 集計が中心、ほぼリアルタイム不要 |
| 別 DB / 検索エンジン | PostgreSQL → Elasticsearch / OpenSearch へ | 検索要件が強い、スケール分離したい |
Read Model 設計の原則
| 原則 | 説明 |
|---|---|
| 画面 1 つにつき Read Model 1 つが基本 | 再利用しようとすると肥大化 |
| 正規化しない | 冗長データを恐れず、JOIN を減らす |
| 書き込み側の集約境界を気にしない | 読み取りは複数集約を横断して OK |
| 古いデータで良い | 結果整合性を業務側と合意する |
🎯 AI への指示に CQRS を組み込む
書き込み側の指示
対象: Sales Context の Order 集約
タスク: キャンセル Command の実装
制約:
- 集約内で完結。他集約を更新しない
- 成功時に OrderCancelled イベントを発行
- Read Model の更新は別 Handler に任せる
読み取り側の指示
対象: 管理画面の「過去 30 日の注文一覧」
タスク: Read Model + Query の実装
制約:
- Order 集約を直接使わない(Read Model 専用テーブルを設ける)
- 集計・整形は DB 側で済ませる
- 結果整合性 OK(数秒の遅延は許容)
こうして 書き込みと読み取りで別セッション・別指示 にすると、AI が境界を混同しない。
レビューチェックリスト
- Query が集約を直接 load していないか
- Command が Read Model を直接更新していないか
- Read Model が書き込み側の不変条件を勝手に守ろうとしていないか
- 画面要件のために集約にフィールドが追加されていないか
- 結果整合性が業務要件に合っているか(決済関連で即時整合性が必要ではないか)
⚖️ CQRS を使わない判断
CQRS は強力だが、コスト高い。使わない方が良い場面も多い。
CQRS が効く場面
- 読み込みと書き込みの負荷比が大きい(読み多数)
- 集計・検索・レポートが多い
- 複雑なビジネスルールが書き込み側にある
- マルチチャネル配信(EC モール連携など)
CQRS が過剰な場面
- 単純な CRUD が主体
- トランザクション一貫性が最重要(決済の同時実行等)
- 小規模チームで学習コストを回収できない
- 画面数が少なく、ドメインモデルがそのままビューに使える
AI 駆動開発のスピードで立ち上げる小さなプロトタイプに CQRS は不要。 規模と複雑さが増してから後付け適用 する選択肢もある。
🧩 マルチモール EC での適用イメージ
CTS-EC 共通商品マスタのように、 共通商品マスタへの更新(Command) と 各モール向け出品ビューの参照(Query) が大きく非対称なドメインは、CQRS の利得が出やすい典型例。
Command 側:
- バリエーション・SKU・画像紐付けなどビジネスルールが厚い
- 共通マスタの不変条件を守る必要がある
Query 側:
- モール別の表示・在庫・配信状態を高速に引きたい
- 「Yahoo 未配信の商品一覧」のような集計が多い
- 画面ごとに違う整形が必要
この非対称性を 1 モデルで受けると、共通商品マスタが画面要件で歪む。CQRS で分けると、AI に「Yahoo 未配信一覧の画面を作って」と指示しても Read Model の追加で完結 し、Command 側の集約は無傷。
🔗 イベントソーシングとの組み合わせ
CQRS は イベントソーシング とよく組み合わせて使われる 。書き込み側でイベントを永続化し、そのイベントを購読して Read Model を構築する流れが典型。
Command → 集約 → イベント追記 → イベント Store
↓
Projection
↓
Read Model
ただし両者は独立した概念で、 CQRS だけ・イベントソーシングだけの採用も成立 する。段階的に導入するなら、まず CQRS だけを入れて、必要になってからイベントソーシングを追加するのが現実的。
⚠️ CQRS 採用時のアンチパターン
1. CQRS + 即時整合性の要求
「書き込んだ直後に読み取りに反映されていないと困る」という業務要件がある場合、Read Model の非同期反映と衝突する。
是正: 業務側と 「どれくらいの遅延を許容するか」 を事前合意。許容できない操作は、書き込み直後に Read Model を同期更新するか、書き込み側から直接読み取る例外ルートを設ける。
2. Command 側への画面要件の混入
「画面に表示するために Order に totalIncludingTax フィールドを追加」といった侵食が起きがち。
是正: 計算・整形は Read Model で行う。集約には業務ルールを守るために必要なフィールドだけ。
3. Read Model の肥大化
全画面を 1 つの Read Model で賄おうとし、巨大な View テーブルが生まれる。
是正: 画面 1 つにつき Read Model 1 つ を基本に戻す。Read Model の重複は許容、正規化より読み取り速度を優先。
4. AI に Read Model を全自動生成させる
「画面から Read Model を作って」と丸投げすると、書き込み側を無視した設計や、他の画面と矛盾する Read Model が量産される。
是正: Read Model 一覧を自分で管理し、 追加時のルール(命名・更新タイミング・テナント分離)を明文化してから AI に任せる。
📚 まとめ
- CQRS は 書き込みと読み取りを別モデルに分離する 設計パターン
- AI 駆動開発での最大の効用は 「画面要件の侵食からドメインモデルを守る」 こと
- Read Model は画面単位で作り、正規化より読み取り速度を優先
- 結果整合性を業務側と合意するのが採用の前提
- 単純 CRUD には過剰。規模・複雑さに応じて選択する
- イベントソーシングとの組み合わせで真価を発揮するが、単独採用も可
🔗 関連記事
- 開発手法ガイド:概要 — シリーズ全体の位置づけ
- 開発手法ガイド:DDD 編 — CQRS の前提となるドメイン設計
- 開発手法ガイド:イベントソーシング編 — CQRS とよく組み合わせる永続化
- 開発手法ガイド:CES アプローチ編 — CQRS + ES + Saga の統合
- CTS-EC 共通商品マスタ — Command/Query 非対称ドメインの実例