Существует значительное сообщество людей, которые используют CQRS для реализации своих доменов. Мне кажется, что если интерфейс вашего хранилища аналогичен лучшим методам, которые они используют, то вы не будете слишком далеко сбиваться с пути.
Основываясь на том, что я видел ...
1) Обработчики команд обычно используют репозиторий для загрузки агрегата через репозиторий. Команды предназначены для одного конкретного экземпляра агрегата; репозиторий загружает корень по ID. Нет, как я вижу, случая, когда команды запускаются для совокупности агрегатов (вместо этого сначала нужно выполнить запрос, чтобы получить коллекцию агрегатов, а затем перечислить коллекцию и выдать команду каждому.
Следовательно, в тех случаях, когда вы собираетесь изменять агрегат, я ожидаю, что хранилище вернет сущность (она же корень агрегата).
2) обработчики запросов вообще не касаются агрегатов; вместо этого они работают с проекциями агрегатов - объектами значений, которые описывают состояние агрегата / агрегатов в определенный момент времени. Так что думайте ProjectionDTO, а не AggregateDTO, и у вас есть правильная идея.
В тех случаях, когда вы будете выполнять запросы к агрегату, готовить его к отображению и т. Д., Я ожидаю увидеть возвращенный 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 зависит от того, что вы ожидаете от потребителя. Я предполагаю, что будет одна конкретная реализация, поддерживающая интерфейс для каждого контекста.
GetCustomerByName('John Smith')
вернется, если в вашей базе данных будет двадцать Джонов Смитов? Похоже, вы предполагаете, что нет двух людей с одинаковыми именами.