TL;DR

コマンドクエリ分離(CQS)の考え方をサービスのレベルまで広げたシステムアーキテクチャ。クエリのモジュールとコマンドのモジュールを分ける。Command Query Responsibility Segregation の略である。

なぜ必要か

クエリが返すデータとコマンドに渡すデータは、同じ型にもできる。しかし、次の理由から別の型にしたほうがよい。

  • 2 つのデータは異なることが多い。クエリは非正規化したデータや計算した値を返すが、コマンドはそれらを使わない。クエリは ID を返すが、コマンドのデータには ID を含められない
  • クエリとコマンドは、それぞれ独立して変わっていく
  • パフォーマンスのために、複数のエンティティをまとめて返すクエリがある

仕組み

型を分けると、それぞれを別のモジュールに置く設計になる。一方がクエリ(ReadModel)のモジュール、もう一方がコマンド(WriteModel)のモジュールである。

type CreateCustomer = WriteModel.Customer -> DbResult<Unit>
type ReadCustomer = CustomerId -> DbResult<ReadModel.Customer>

この考え方はデータベースにも当てはめられる。読み込みに最適化したデータストア(非正規化、多くのインデックス)と、書き込みに最適化したデータストア(インデックスなし、トランザクションあり)を用意する。永続化にはイベントソーシングが使われることもある。

注意点

物理的なデータベースを 2 つに分ける必要はない。たとえば「書き込み」のモデルを単純なテーブルにし、「読み取り」のモデルをそのテーブルのビューにする方法もある。

物理的に分けると、「書き込み」のストアから「読み取り」のストアへデータをコピーする仕組みが必要になる。「読み取り」のストアのデータは古くなることがあり、結果整合性になる。ドメインによってはこれが問題になるので、分けることによる設計上のメリットが見合うかを判断する。

自分の理解

Wikipedia の説明がすっと入ってきた。オブジェクト指向プログラミングでいえば、インタフェースを分けるイメージである。

関連

出典