CTS-KB

開発手法ガイド:CQRS 編 — AI に複雑クエリを任せても壊れない設計

⏱ 約 7 分で読めます
#開発手法 #CQRS #AI駆動開発 #Read Model #結果整合性

🤖 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。

CQSCQRS
メソッド単位で分離モデル・経路・場合によっては 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 ViewDB の 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 には過剰。規模・複雑さに応じて選択する
  • イベントソーシングとの組み合わせで真価を発揮するが、単独採用も可

🔗 関連記事

📖 関連用語