Istnieje spora społeczność ludzi, którzy używają CQRS do wdrażania swoich domen. Mam wrażenie, że jeśli interfejs twojego repozytorium jest analogiczny do najlepszych praktyk przez nich stosowanych, to nie zbłądzisz zbyt daleko.
Na podstawie tego, co widziałem ...
1) Programy obsługi poleceń zwykle używają repozytorium do ładowania agregacji za pośrednictwem repozytorium. Polecenia są ukierunkowane na jedną konkretną instancję agregatu; repozytorium ładuje katalog główny według identyfikatora. Nie ma, jak widzę, przypadku, w którym polecenia są uruchamiane względem kolekcji agregatów (zamiast tego najpierw należy uruchomić zapytanie, aby uzyskać kolekcję agregatów, a następnie wyliczyć kolekcję i wydać polecenie każdemu z nich.
Dlatego w kontekstach, w których zamierzasz modyfikować agregację, oczekiwałbym, że repozytorium zwróci encję (inaczej root root agregatu).
2) Programy obsługi zapytań w ogóle nie dotykają agregatów; zamiast tego pracują z rzutami obiektów o wartościach agregujących, które opisują stan agregatu / agregatów w pewnym momencie. Pomyśl więc ProjectionDTO zamiast AggregateDTO, a masz dobry pomysł.
W kontekstach, w których zamierzasz uruchamiać zapytania względem agregatu, przygotowując je do wyświetlenia itd., Spodziewam się, że zostanie zwrócone DTO lub kolekcja DTO, a nie jednostka.
Wszystkie twoje getCustomerByProperty
rozmowy wyglądają dla mnie jak zapytania, więc należą do tej drugiej kategorii. Prawdopodobnie chciałbym użyć jednego punktu wejścia do wygenerowania kolekcji, więc chciałbym sprawdzić, czy
getCustomersThatSatisfy(Specification spec)
jest rozsądnym wyborem; procedury obsługi zapytań konstruują następnie odpowiednią specyfikację na podstawie podanych parametrów i przekazują tę specyfikację do repozytorium. Minusem jest to, że podpis naprawdę sugeruje, że repozytorium jest kolekcją w pamięci; nie jest dla mnie jasne, że predykat dużo kupuje, jeśli repozytorium jest jedynie abstrakcją uruchamiania instrukcji SQL względem relacyjnej bazy danych.
Istnieją jednak pewne wzorce, które mogą pomóc. Na przykład zamiast ręcznie budować specyfikację, przekaż do repozytorium opis ograniczeń i pozwól implementacji repozytorium zdecydować, co należy zrobić.
Ostrzeżenie: wykryto pisanie 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);
}
}
Podsumowując: wybór między dostarczeniem agregatu a dostarczeniem DTO zależy od tego, czego oczekujesz od konsumenta. Sądzę, że będzie to jedna konkretna implementacja obsługująca interfejs dla każdego kontekstu.
GetCustomerByName('John Smith')
zwróci, jeśli masz w bazie dwudziestu John Smiths? Wygląda na to, że zakładasz, że nie ma dwóch osób o tym samym imieniu.