Образцовый шаблон - как правильно обрабатывать JOINs и сложные запросы?

У меня проблема с шаблоном репозитория - как выполнять операции JOIN между несколькими репозиториями. В этом проекте мы используем MVC, EF, DDD. Я знаю, что такого рода вопрос был здесь несколько раз, я упоминаю эти вопросы позже в этом.

Между общей моделью репозитория (IRepository) и конкретной моделью репозитория я выбрал конкретную опцию, так как я рассматриваю ORM (в нашем случае EF) как общий шаблон репозитория, поэтому не имеет смысла добавлять другой общий репозиторий и мы предпочли бы привязать репозиторий к потребностям домена.

Проблема в том, что у меня есть несколько (~ 10) таблиц, каждая со многими строками (миллионы), и мне нужно выполнить JOINs, поэтому использование IList или IEnumerable не является жизнеспособным вариантом.

Мое понимание (и моя перспектива) заключается в том, что IQueryable не должен покидать репозиторий ( "Что происходит в DAL, должен оставаться в DAL" ). Было бы проще разоблачить IQueryable и использовать его в LINQ в службе, но он сильно нарушает разделение проблем и подрывает роль репозиториев - в этом случае служба будет делать то же самое, что и репозиторий. Чтобы выбрать несколько, эти статьи поддерживают эту перспективу (или, скорее, убеждение):

Чтобы вернуть IQueryable <T> или не вернуть IQueryable <T>

Должен ли я возвращать IEnumerable <T> или IQueryable <T> из моего DAL?

http://www.shawnmclean.com/blog/2011/06/iqueryable-vs-ienumerable-in-the-repository-pattern/

http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/

Существуют также аналогичные вопросы и решения, например. Как присоединиться к нескольким таблицам с использованием шаблона репозитория и платформы Entity Framework, которые предлагают .Include(), но это не вариант для загруженных больших таблиц и объединений во многих таблицах - с каждый JOIN, мы используем subselects, чтобы ограничить то, что фактически присоединилось).

Этот вопрос (ответ и комментарии) - Как я могу запросить кросс-таблицы с шаблоном репозитория? - в основном предлагает дифференциацию на основе задач: создание одного репозитория для запросов с JOINS и "обычные" репозитории для манипуляций с каждым объектом.

Я вижу, что у нас есть следующие опции:

  • Предоставление IQueryable и выполнение сложных запросов JOIN в сервисах; Я искренне чувствую, что это анти-шаблон, мне это не нравится.
  • Не используйте репозиторий для этих ~ 10 таблиц и выполняйте запросы в службах; некоторые статьи предположили, что использование EF достаточно (например, Можно ли обойти шаблон репозитория для сложных запросов?), я не согласен с этим.
  • Использовать дифференциацию на основе задач, не ограничивать репозитории 1:1 repo: entity (я сторонник этой опции)
  • Что-то совсем другое?

Итак - что бы вы предложили? Снова и снова, спасибо.

Ответ 1

  • означает утечку в приложение - антипаттерн, спагетти-код.
  • как он отличается от (1) точно? Еще одна проблема.
  • немного ближе...
  • Используйте шаблон Объект запроса. Инкапсулируйте сложный запрос в объект на основе задачи, который находится рядом с репозиториями. Он может возвращать DTO, оптимизированные для просмотра, а не для объектов домена.
  • В значительной степени полагаясь на QO, вы попадаете в архитектуру под названием CQRS - Сегментация ответственности за выполнение команд-запросов.

Еще одна вещь. Для объекта: repo нет соответствия 1:1. Только Агрегаты должны иметь репозиторий, а не каждую единицу.