DDD - Как реализовать высокопроизводительные репозитории для поиска
У меня есть вопрос относительно DDD и шаблона хранилища.
Скажем, у меня есть репозиторий Customer для совокупного корня Customer. Методы Get & Find возвращают полностью заполненный агрегат, который включает в себя такие объекты, как Address и т. Д. Все хорошо. Но когда пользователь ищет клиента в пользовательском интерфейсе, мне просто требуется "сводка" совокупности - просто плоский объект с обобщенной информацией.
Один из способов справиться с этим - вызвать метод find в репозитории как обычно, а затем на уровне приложения сопоставить каждый клиентский агрегат с DTO CustomerSearchResult / CustomerInfo и отправить их обратно клиенту.
Но моя проблема с этим - производительность; каждому агрегату клиентов может потребоваться несколько запросов для заполнения всех ассоциаций. Так что, если мои критерии поиска соответствовали 50 клиентам, это довольно большой удар по БД для потенциального извлечения данных, которые мне даже не понадобятся.
Другая проблема заключается в том, что я могу включить обобщенные данные о клиенте, которые находятся за пределами общей корневой границы клиента, например, дату последнего сделанного заказа. У заказа есть своя собственная совокупность, и поэтому, чтобы получить информацию о заказе клиента, мне нужно будет вызвать OrderRepository, что также снижает производительность.
Так что теперь я думаю, что у меня есть два варианта:
Добавьте дополнительный метод Find в CustomerRepository, который возвращает список этих сводных объектов, выполняя один эффективный запрос.
Создайте специально созданную для чтения CustomerInfoRepository, которая имеет метод find, описанный в 1.
Но оба они чувствуют, что я иду против принципов DDD. Мои репозитории наследуются от общей базы: Репозиторий, где T: IAggregateRoot. Эти объекты сводной информации не являются агрегатами и имеют тип, отличный от T, так что на самом деле #1 идет вразрез с дизайном.
Возможно, для #2 я бы создал абстрактный SearchRepository без ограничения IAggregateRoot?
В моем домене много похожих сценариев.
Как бы вы реализовали этот сценарий?
Спасибо дэйв
Обновить
Прочитав ответ Тео, я думаю, что пойду с опцией #2 и создам специализированный SearchRepository в моей инфраструктуре, ориентированной на эти сценарии. Прикладной уровень (службы WCF) может затем вызывать эти репозитории, которые просто заполняют сводные DTO напрямую, а не отображают сущности домена в DTO.
**** Обновление 2 ****
Хотя я спросил об этом более года назад, я подумал, что просто добавлю, что с тех пор я обнаружил CQRS, который нацелен на решение именно этой проблемы. Уди Даан ( http://www.udidahan.com/) и Грег Янг ( http://codebetter.com/gregyoung/) много писали об этом. Если вы создаете распределенное приложение с DDD, CQRS для вас!
2 ответа
Я думаю, что вы хотите отображать только обобщенную информацию. Эти биты обобщенной информации не являются ни сущностями, ни объектами-значениями модели предметной области. Они только информация, не более того.
Это что-то вроде показа отчетной информации. Если бы я имел дело с такими вещами, я бы не стал придерживаться чистого подхода DDD. Ваши предложенные варианты в порядке, потому что это делает вашу работу. DDD не следует рассматривать как догму. Думай нестандартно. Ослабьте немного DDD.
Но имейте в виду, что вы просто создаете информационные значения вне модели для отображения цели. Поэтому, если пользователь выбирает один бит информации для выполнения какой-либо операции с ним (что определено в модели предметной области), вам необходимо извлечь идентификатор из информационных значений и извлечь объект / совокупность объекта / значения из репозитория.
Я настоятельно рекомендую это видео: Эрик Эванс: Что я узнал о DDD со времени появления книги. Если вы читаете его книгу, вы действительно должны увидеть все видео. Обратите особое внимание на время около 30:00, когда сам Эрик Эванс говорит об агрегатах и обращается к проблеме, которая у вас есть в данный момент.
Я мог бы:
- Возвратите другой объект, который представляет представление моего объекта для отображения, например CustomerInfo.
- Вернуть таблицу данных. Часто универсальный контейнер - самый простой и лучший способ пойти.
Если буква T в вашем базовом репозитории - это Клиент, то я думаю, что вы неправильно применяете концепцию совокупных корней, хотя я не являюсь строгим Evansangelist. Я разработал бы репозиторий для Клиента, который возвращал бы любые данные, которые логически или удобно группируются с Клиентом, включая DataTables или объекты только для чтения, которые являются представлениями данных Клиента.