この記事は
「ドメイン駆動設計入門」を読んでまとめている第6弾
前回記事はこちら
今回は仕様についてまとめる
仕様
仕様はオブジェクトの評価を行うオブジェクトです(P293)
簡単な評価はメソッドに記述される
たとえば user.Age < 20
で成人であるか評価をする、など。
評価はドメインにとどめておくべきルールであるにもかかわらず
複雑な評価の手順がアプリケーションサービスに記述されることがある
(ドメインオブジェクトを束ねているアプリケーションサービスはできることが多いので、放っておくと自然とそこにルールが書かれがち)
ドメインのルールがアプリケーションに漏れ出る、これは避けねばならない
そんなときには仕様オブジェクトをつかう
仕様オブジェクトは、自分ではないオブジェクトを評価する
エンティティはリポジトリを操作したくない
評価:Circle
はプレミアムUser
数によってサークル人数上限を決めたい
-
エンティティの
Circle
はUserRepository
を持つべきではない。ドメインの表現に徹するべきである -
Circle
はリポジトリの操作を可能な限り避ける必要がある -
かといって、
UserRepository
をコンストラクタで受け取るCircleApplicationService
が評価をすべきではない。ルールはドメインにとどめておくべきである
仕様が代わりにリポジトリを操作する
仕様の実例はこちら
仕様はオブジェクトの評価のみを行う
実例ではサークルが満員であるかどうかの評価だけを行っている
評価まみれになったオブジェクト
評価が増えると依存が増えて、変更に伴う痛みも増加する
積極的に仕様として切り出すことで事態を避ける
リポジトリの使用を避ける
実は仕様もれっきとしたドメインオブジェクトである(ルールが書かれている)
ドメインオブジェクトは入出力(リポジトリの操作)はしたくないというのが本音
ファーストクラスコレクションをつかってリポジトリの使用を避けることができる
だれがリポジトリを操作するか?→アプリケーションサービスが操作する
アプリケーションサービスでファーストクラスコレクションへデータの詰め替え作業が発生する
ドメインオブジェクトから入出力(リポジトリの操作)を排除するために
ファーストクラスコレクションは有力な解決方法となり得る
リポジトリに仕様をわたす
「おすすめサークル」の評価を実装であるリポジトリに書くべきではない
ドメインのルールはインフラに漏れないよう常に注意する
リポジトリに仕様をわたすデメリット
リポジトリに仕様をわたして、仕様を満たすレコードを取得するためには、全件検索が必要になる
件数によってはシステム利用者が求める性能を満たせなくなる可能性がある
var result = repository.Find(/*仕様*/).Take(10).ToList();
C#のLinq
C#のLinqをつかえば、.ToList()
でコレクションを確定するまでは遅延実行ができるので、パフォーマンスとリポジトリの使用を両立できる
var allRecords = repository.FindAll(); // この時点ではデータを取得してはない
var result = allRecords.Skip().Take().ToList(); // skipされてtakeされたあと初めてデータが確定する