Можно ли обойти шаблон репозитория для сложных запросов?

Это мое понимание DDD на данный момент:

  • Строгий шаблон репозитория должен реализовывать только функции get(), delete() и create() и, возможно, варианты get(), в которых можно искать или извлекать всю коллекцию
  • Обычно для каждого агрегатного корня есть один репозиторий

(из исследования, я знаю, что они не всегда принимают нормы)

Вопрос заключается в том, как реализовать сложные запросы, которые включают в себя множество совокупных корней. Например, у нас есть два совокупных корня - продукт и пользователь. Если я делаю страницу, в которой перечислены продукты, которые пользователь купил, у меня есть запрос, который растягивается как по агрегату пользователя, так и по агрегату продукта.

Как этот запрос должен быть реализован?

  • Теперь я должен иметь репозиторий для этого запроса и запросов со связанными функциями (некоторые не согласятся и говорят, что репозиторий не является слоем запроса).

  • Используйте только репозиторий для продукта и пользователя, захватывайте все записи и делайте все в памяти (это звучит неправильно)

  • У вас есть запрос (LINQ или SQL) внутри службы, не используя репозиторий, связанный с агрегатами вообще.

Есть ли другие способы?

Ответ 1

Строгий шаблон репозитория должен реализовывать только функции get(), delete() и create(), и, возможно, варианты get(), где можно искать или получить целую коллекцию

Интерфейс репозитория является частью вашего домена и должен быть максимально основан на Ubiquitous Language. Все репозитории отличаются друг от друга, как и все ваши Агрегаты разные. Строгие, общие хранилища являются чрезмерной генерацией CRUD и могут уменьшить экспрессивность кода. Метод "Создать" также не относится к репозиторию, потому что начало жизненного цикла объекта обычно обрабатывается Factory или самим объектом. "Добавить" похоже на лучшее имя, если вы хотите сохранить существующий объект, потому что у репозитория есть семантика коллекции.

Вопрос в том, как реализовать сложные запросы, которые включают многие совокупные корни. Например, у нас есть два совокупных корня - продукта и пользователя. Если я делаю страницу, в которой перечисляются, какие продукты пользователь купили, тогда у меня есть запрос, который растягивается на обоих пользователей агрегат и агрегат продукта.

В этом случае вам просто нужно выслушать бизнес-требования, я подчеркнул ту часть, которая, по моему мнению, является самой важной. Исходя из этого, похоже, что вам нужно:

Products products = /* get Products repository implementation */;
IList<Product> res = products.BoughtByUser(User user);

Идея организации такого кода заключается в том, чтобы максимально соответствовать требованиям бизнеса и вездесущему языку. Именование интерфейсов репозитория также важно, я предпочитаю Продукты или AllProducts вместо ProductsRepository. Фил Кальсадо, очень хорошая статья по этому вопросу, настоятельно рекомендуется.

How should this query be implemented?

В этом запросе нет ничего особенного, он может быть реализован так же, как и все другие запросы в репозитории продуктов. Сам запрос скрыт от домена, поскольку реализация Репозитория принадлежит уровню доступа к данным. Data Access может реализовать любой запрос, потому что он имеет глубокое знание всех Агрегатов и их отношений. На данный момент это будет просто вопрос Hibernate или SQL.

Ответ 2

Прежде всего, запросы редко выполняются против Aggregate Roots. Они выполняются против данных и возвращают только данные. Репозитории - очень удобные абстракции настойчивости для использования в прикладном уровне (команды и такой) код. Они нам нужны, потому что мы хотим иметь возможность протестировать этот слой без необходимости в базе данных. Поэтому, чем меньше репозиторий, тем лучше - его легче издеваться над ним.

Я имею тенденцию использовать специализированные объекты Finder, которые позволяют моему пользовательскому интерфейсу запрашивать хранилище данных. Я даже размещаю свои Finders в слое пользовательского интерфейса. Дело в том, что они имеют тенденцию меняться каждый раз, когда меняется пользовательский интерфейс, поэтому лучше соединить их. Еще одна веская причина, по которой вы не хотите размещать методы запросов в репозитории, - это репозиторий, который является частью вашего домена, вашего вездесущего языка. Вы не хотите загрязнять их концепциями пользовательского интерфейса, которые, как правило, недолговечны и быстро меняются.

Я написал сообщение в блоге, объясняющее эту концепцию некоторое время назад. Вы можете найти здесь здесь.

Ответ 3

Просто поместите его в класс репозитория. Это Get, вероятно, в ProductRepository, так как это то, что он возвращает. GetProductsByUser (int UserID). Или, если у вас есть архитектура n-уровня, это может быть в методе службы.

Я всегда думал о том, что репозиторий является местом для размещения любых методов доступа к базе данных, поскольку идея представляет собой отдельный код базы данных из всего остального.