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