Ada komunitas orang yang cukup besar yang menggunakan CQRS untuk mengimplementasikan domain mereka. Perasaan saya adalah bahwa, jika antarmuka repositori Anda analog dengan praktik terbaik yang digunakan oleh mereka, Anda tidak akan tersesat terlalu jauh.
Berdasarkan apa yang saya lihat ...
1) Penangan perintah biasanya menggunakan repositori untuk memuat agregat melalui repositori. Perintah menargetkan contoh spesifik agregat; repositori memuat root dengan ID. Tidak ada, yang bisa saya lihat, kasus di mana perintah dijalankan terhadap kumpulan agregat (sebagai gantinya, Anda pertama-tama akan menjalankan kueri untuk mendapatkan kumpulan agregat, kemudian menghitung koleksi dan mengeluarkan perintah untuk masing-masing.
Oleh karena itu, dalam konteks di mana Anda akan memodifikasi agregat, saya berharap repositori mengembalikan entitas (alias akar agregat).
2) Penangan permintaan tidak menyentuh agregat sama sekali; sebagai gantinya, mereka bekerja dengan proyeksi agregat - objek nilai yang menggambarkan keadaan agregat / agregat pada beberapa titik waktu. Jadi pikirkan ProjectionDTO, daripada AggregateDTO, dan Anda memiliki ide yang tepat.
Dalam konteks di mana Anda akan menjalankan kueri terhadap agregat, menyiapkannya untuk ditampilkan, dan seterusnya, saya berharap untuk melihat DTO, atau koleksi DTO, dikembalikan, bukan entitas.
Semua getCustomerByProperty
panggilan Anda tampak seperti pertanyaan bagi saya, sehingga mereka akan masuk ke dalam kategori yang terakhir. Saya mungkin ingin menggunakan satu titik masuk untuk menghasilkan koleksi, jadi saya akan mencari tahu apakah
getCustomersThatSatisfy(Specification spec)
adalah pilihan yang masuk akal; penangan permintaan kemudian akan membangun spesifikasi yang sesuai dari parameter yang diberikan, dan meneruskan spesifikasi itu ke repositori. The downside adalah bahwa tanda tangan benar-benar menunjukkan bahwa repositori adalah kumpulan dalam memori; tidak jelas bagi saya bahwa predikat membeli Anda banyak jika repositori hanyalah abstraksi menjalankan pernyataan SQL terhadap database relasional.
Ada beberapa pola yang bisa membantu. Sebagai contoh, alih-alih membangun spesifikasi dengan tangan, sampaikan ke repositori deskripsi kendala, dan biarkan implementasi repositori untuk memutuskan apa yang harus dilakukan.
Peringatan: java seperti mengetik terdeteksi
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);
}
}
Kesimpulannya: pilihan antara menyediakan agregat dan menyediakan DTO tergantung pada apa yang Anda harapkan konsumen lakukan dengannya. Dugaan saya akan menjadi salah satu implementasi konkret yang mendukung antarmuka untuk setiap konteks.
GetCustomerByName('John Smith')
kembali jika Anda memiliki dua puluh John Smiths di basis data Anda? Sepertinya Anda mengasumsikan tidak ada dua orang yang memiliki nama yang sama.