ドメインを実装するためにCQRSを使用する大規模なコミュニティがあります。私の考えでは、リポジトリのインターフェイスがそれらのリポジトリで使用されているベストプラクティスに類似していれば、あなたはあまりにも迷わないでしょう。
私が見たものに基づいて...
1)通常、コマンドハンドラーはリポジトリを使用して、リポジトリを介して集約をロードします。コマンドは、集約の単一の特定のインスタンスを対象としています。リポジトリはIDでルートをロードします。コマンドが集計のコレクションに対して実行されるケースはありません(代わりに、最初に集計のコレクションを取得するクエリを実行してから、コレクションを列挙し、それぞれにコマンドを発行します)。
したがって、集約を変更するコンテキストでは、リポジトリがエンティティ(集約ルート)を返すことが期待されます。
2)クエリハンドラーは集計にまったく触れません。代わりに、それらは集約の投影で動作します-ある時点での集約/集約の状態を記述する値オブジェクト。したがって、AggregateDTOではなくProjectionDTOを考えてください。正しい考えがあります。
集計に対してクエリを実行したり、表示用に準備したりするコンテキストでは、エンティティではなく、DTOまたはDTOコレクションが返されることを期待しています。
あなたのgetCustomerByProperty
呼び出しはすべて私にとってクエリのように見えるので、後者のカテゴリに分類されます。おそらく、単一のエントリポイントを使用してコレクションを生成したいと思うので、
getCustomersThatSatisfy(Specification spec)
合理的な選択です。クエリハンドラは、指定されたパラメータから適切な仕様を構築し、その仕様をリポジトリに渡します。欠点は、署名が本当にリポジトリがメモリ内のコレクションであることを示唆していることです。リポジトリがリレーショナルデータベースに対してSQLステートメントを実行するだけの抽象化である場合、述語が多くを購入するかどうかは明確ではありません。
ただし、役立つパターンがいくつかあります。たとえば、手作業で仕様を作成する代わりに、制約の説明をリポジトリに渡し、リポジトリの実装が何をすべきかを決定できるようにします。
警告:Javaのような入力が検出されました
interface CustomerRepository {
interface ConstraintBuilder {
void setLastName();
void setFirstName();
}
interface ConstraintDescriptor {
void copyTo(ConstraintBuilder builder);
}
List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor);
}
SQLBackedCustomerRepository implements CustomerRepository {
List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
WhereClauseBuilder builder = new WhereClauseBuilder();
descriptor.copyTo(builder);
Query q = createQuery(builder.build());
//...
}
}
CollectionBackedCustomerRepository implements CustomerRepository {
List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
PredicateBuilder builder = new PredicateBuilder();
descriptor.copyTo(builder);
Predicate p = builder.build();
// ...
}
class MatchLastName implements CustomerRepository.ConstraintDescriptor {
private final lastName;
// ...
void copyTo(CustomerRepository.ConstraintBuilder builder) {
builder.setLastName(this.lastName);
}
}
結論として、集約を提供するかDTOを提供するかの選択は、消費者がそれを使用して何を期待するかによって異なります。私の推測では、各コンテキストのインターフェイスをサポートする具体的な実装の1つです。
GetCustomerByName('John Smith')
データベースに20人のJohn Smithがいる場合、何が返されますか?2人の人が同じ名前を持っているとは思わないようです。