はじめに
CQRSは決してDDDのみで用いられる設計パターンという訳ではありません。しかし、一覧画面の表示のように複数の集約から値を取り出すような場面ではDDDで効果を発揮する設計パターンになります。
※初学者の学習メモです。
課題
DDDの実装パターンでは、基本的には永続化層との入出力はRepositoryを使うことになります。更新系の処理ではEntityやValue Objectでドメインの知識を表現し、Repositoryを使って集約単位で永続化するとう構成をとるとメンテナンス性が高まります。
一方、参照系の処理、特に一覧画面では、複数集約の値を組み合わせた結果を画面に返すことが多くなります。
もし、複数の集約がありそれぞれにRepositoryがある場合、1つのUseCase(Application Service)で実装しようとすると、Repositoryからそれぞれの値を取得し、戻り値のオブジェクトに詰め替えるような実装にせざるを得ません。
このデメリットとして、
- 複数の集約から値を取得して戻り値に詰め替える処理が、ループが増えて読みにくくなる
- 画面に返す必要のない値を一度取得するのでパフォーマンスが悪化する
- 複数集約の条件(where)で絞り込んでのページングができない
などが挙げられます。
対策
CQRS(コマンドクエリ責務分離)を導入します。CQRSは「情報の参照に使用するオブジェクトと更新に使用するオブジェクトは異なるものを使う」アーキテクチャになります。
書き込み(コマンド)と読み込み(クエリ)は別々に取り扱うべきと考える設計方法。
- 書き込み->データの入力のような記憶領域に保管すること。
- 読み込み->データの出力で、記憶領域からデータを取り出すこと。
DDDのアーキテクチャでは、QueryServiceのInterfaceとをUseCase層に、実装クラスはRepositoryと同様にインフラ層に配置します。UseCaseは抽象的な知識だけ持ち、実装の知識はインフラ層に隠蔽します。
上記図の更新系では、Repositoryを用いて集約単位でデータの永続化を行い、参照系はQueryServiceで複数のテーブルをjoinして取得するクエリを書き、シンプルに結果をDTOに詰め替えるような構成にします。
これによって、
- 複数集約にまたがるデータを取得する際のコードがシンプルになりメンテナンス性が高まる
- クエリパフォーマンスが上がり、チューニングしやすくなる
- 複数集約の条件(where)で絞り込んでのページングができるようになる
などが見込めます。
参考文献: https://little-hands.hatenablog.com/entry/2019/12/02/cqrs
PHPフレームワークLaravel Webアプリケーション開発