リポジトリにドメイン側の処理を埋め込んでしまうのも問題です。(例えば、商品を発売日順に並び替えるというのは仕様になります。)
対策としては、レポジトリ側に仕様オブジェクトを渡して、レポジトリではfindAllで単純に情報を取得して仕様オブジェクトの評価メソッドでフィルタリングをかけるように設計します。そうすることでレポジトリ側に仕様が混在することを防げます。
ただし、こうした処理は数万件とかある場合にパフォーマンスが悪くなります。
問題点
レポジトリでは仕様を混在させられず単純なSQLしか発行できないのでfindAllで全件取得することでパフォーマンスが劣化する。
仕様オブジェクト側でレポジトリのfindAllで全件取得したデータの中から1件1件仕様に合うデータかどうかチェックしていくのは非常にパフォーマンスが悪くなってしまいます。それに1件1件インスタンスを生成してもほとんどが使われずに破棄されるのが関の山でしょう。
また、仕様オブジェクト側で上記処理をループさせようもんならさらにパフォーマンスは劣化していくことになります。(本来はSQLのJOINでやらないといけないような処理)
対策
基本的にパフォーマンスが悪くなるのは「クエリ(読み取り)」の処理になります。反対に「コマンド(書き込み)」はドメイン上の制約が多く存在します。
コマンドにおいてはドメインオブジェクトを積極的に利用して、クエリにおいてはパフォーマンス問題がつきまとうのである程度ドメインオブジェクトの利用を緩和してクライアントが利用しやすい形で提供することが重要です。この考え方をCQRSと呼びます。
複雑なSQLの場合は、リポジトリを使わずにクエリサービスというオブジェクトを作るようにします。
そもそも、クエリに関しては要求されるデータは複雑で重い処理なのですが、ドメイン知識的には単なる読み取りなので複雑なロジックなどは存在しないのが普通です。(書き込みは逆)なのでこうした技を使っていきましょうというのが昨今のマイクロサービスやDDD化の流れの一つになります。
実装
「UserQueryService」などのクラスを作ってそれを通じて情報を取得するようにします。ここには具体的なSQLを記述してもよいです。リポジトリ経由ではなく、クエリサービス経由で情報を取得します。
もちろん、ORマッパーなどを使えるパッケージなどを利用しているのであればORマッパーでSQLの最適化を目指しても問題ございません。
この記事へのコメントはありません。