CQRSとは? 読み取りと書き込みを分離する
CQRS (Command Query Responsibility Segregation) は、システムをデータを変更する側 (コマンド) と読み取る側 (クエリ) の別々のモデルに分割します。
ほとんどのシステムは読み取りと書き込みの両方に一つのモデルを使います。CQRSは意図的にそれらを分離します。状態を変更するコマンドは一つの経路とモデルを通り、状態を読むクエリは別の、しばしば異なる最適化がされた経路を通ります。複雑なドメインを大幅に単純化できますが、テストとデプロイに現れる可動部を増やします。
コマンドとクエリの分離
コマンドは状態を変更する意図を表し、ほとんど何も返しません。クエリは状態を読み、何も変更しません。CQRSはそれぞれに独自のモデルを与えるため、書き込み側はビジネスルールを強制でき、読み取り側は純粋に高速な取得のために形作られます。
分離する理由
- 読み取りと書き込みのワークロードは独立してスケールできます。
- read modelは高速なクエリのために非正規化できます。
- 書き込みモデルは不変条件と検証に集中できます。
- event sourcingと自然に組み合わさります。
整合性のコスト
read modelが書き込み側から非同期に更新される場合、両者は短時間ずれることがあります。この結果整合性は設計しユーザーに提示する必要があり、意図的に扱わなければ紛らわしいテスト失敗の頻繁な原因になります。
二つのモデルをテストする
CQRSはテスト対象が増えることを意味します。command handlerとその不変条件、query handlerとそのprojection、そしてread modelを最新に保つ伝播です。統合テストは、書き込みとread modelがそれを反映するまでのラグを考慮しなければなりません。
読み取り側と書き込み側のデプロイ
両側は分離しているため独立してデプロイ・スケールできますが、read modelを供給するイベントやprojectionへの変更は互換を保たねばならず、さもなければデプロイ中にprojectionを再構築することになります。
使わないほうがよい場合
CQRSは単純なCRUDにはオーバーヘッドです。読み取りと書き込みのニーズが本当に分かれるとき、あるいは一つの共有モデルが絡まってしまうほどドメインが複雑なときに使いましょう。そうでなければ、余分なパイプラインとテストは割に合いません。
重要なポイント
- CQRSは、コマンド (書き込み) とクエリ (読み取り) に別々のモデルを使います。
- 結果整合性を代償に、独立したスケールと最適化されたread modelを可能にします。
- 単純なCRUDにはオーバーヘッドであり、主に複雑なドメインで報われます。