Я углубляюсь в Domain Driven Design, и некоторые концепции, с которыми я сталкиваюсь, имеют большой смысл на поверхности, но когда я думаю о них больше, я должен задаться вопросом, действительно ли это хорошая идея.
Например, концепция Агрегатов имеет смысл. Вы создаете небольшие домены собственности, чтобы вам не приходилось иметь дело со всей моделью домена.
Однако, когда я думаю об этом в контексте веб-приложения, мы часто обращаемся к базе данных, чтобы извлечь небольшие подмножества данных. Например, на странице может быть указано только количество заказов с ссылками, по которым можно перейти, чтобы открыть заказ и увидеть его идентификаторы.
Если я понимание Аггрегатов правильно, я обычно использую шаблон репозитория вернуть OrderAggregate , который будет содержать элементы GetAll
, GetByID
, Delete
и Save
. ОК, это звучит хорошо. Но...
Если я вызову GetAll для перечисления всех своих заказов, мне будет казаться, что для этого шаблона потребуется вернуть весь список совокупной информации, полные заказы, строки заказа и т. Д. Когда мне нужно только небольшое подмножество этой информации (только информация заголовка).
Я что-то пропустил? Или какой-то уровень оптимизации вы бы использовали здесь? Я не могу представить, чтобы кто-то выступал за возвращение всей совокупности информации, когда она вам не нужна.
Конечно, можно создать методы в вашем репозитории, как GetOrderHeaders
, но это, кажется, сводит на нет цель использования шаблона, подобного репозиторию, в первую очередь.
Кто-нибудь может уточнить это для меня?
РЕДАКТИРОВАТЬ:
После долгих исследований, я думаю, что разобщенность здесь заключается в том, что чистый шаблон репозитория отличается от того, что большинство людей считает репозиторием существующим.
Фаулер определяет хранилище как хранилище данных, которое использует семантику коллекции и обычно хранится в памяти. Это означает создание целого графа объектов.
Эванс изменяет Репозиторий, чтобы включить Агрегированные Корни, и, таким образом, репозиторий ампутируется, чтобы поддерживать только объекты в Агрегате.
Большинство людей, кажется, считают репозитории прославленными объектами доступа к данным, где вы просто создаете методы для получения любых данных, которые вы хотите. Это не похоже на намерение, описанное в паттернах корпоративной архитектуры приложений Фаулера.
Третьи считают, что хранилище - это простая абстракция, используемая в первую очередь для того, чтобы упростить тестирование и копирование или отделить постоянство от остальной системы.
Я предполагаю, что ответ заключается в том, что это гораздо более сложная концепция, чем я сначала думал.
источник
Ответы:
Не используйте вашу модель домена и агрегаты для запросов.
На самом деле, то, что вы спрашиваете, является достаточно распространенным вопросом, чтобы был создан набор принципов и шаблонов, чтобы избежать именно этого. Это называется CQRS .
источник
Я боролся и до сих пор борюсь с тем, как наилучшим образом использовать шаблон репозитория в дизайне, управляемом доменом. После первого использования я пришел к следующим методам:
Хранилище должно быть простым; он отвечает только за хранение доменных объектов и их получение. Вся остальная логика должна быть в других объектах, таких как фабрики и доменные службы.
Хранилище ведет себя как коллекция, как если бы она была в коллекции совокупных корней в памяти.
Репозиторий не является универсальным DAO, каждый репозиторий имеет свой уникальный и узкий интерфейс. Хранилище часто имеет специальные методы поиска, которые позволяют вам искать коллекцию в терминах домена (например: дать мне все открытые ордера для пользователя X). Сам репозиторий может быть реализован с помощью универсального DAO.
В идеале методы поиска возвращают только совокупные корни. Если это неэффективно, он также может возвращать объекты значений только для чтения, которые содержат именно то, что вам нужно (хотя это плюс, если эти объекты значений также могут быть выражены в терминах домена). В качестве последнего средства хранилище также можно использовать для возврата подмножеств или наборов подмножеств совокупного корня.
Подобный выбор зависит от используемых технологий, так как вам нужно найти способ наиболее эффективно выразить модель вашего домена с помощью используемых технологий.
источник
Я не думаю, что ваш метод GetOrderHeaders побеждает цель хранилища вообще.
DDD занимается (помимо прочего) обеспечением того, чтобы вы получали то, что вам нужно, через совокупный корень (например, у вас не было бы OrderDetailsRepository), но он не ограничивает вас в способе упоминания.
Если OrderHeader является концепцией домена, то вы должны определить его как таковой и иметь соответствующие методы репозитория для их получения. Просто убедитесь, что вы проходите правильный корень агрегата, когда вы это делаете.
источник
Мое использование DDD не может считаться «чистым» DDD, но я адаптировал следующие стратегии реального мира, используя DDD для хранилища данных БД.
** Вам не нужно возвращать всю совокупность. Однако, если вы хотите больше, вы должны спросить рут, а не какой-либо другой сервис или репозиторий. Это ленивая загрузка и может быть выполнена вручную с ленивой загрузкой (вставка соответствующего репозитория / службы в корень) или с помощью ORM, который поддерживает это.
В вашем примере я бы, вероятно, предоставил бы вызов репозитория, который принес бы только заголовки заказа, если бы я хотел загрузить детали по отдельному вызову. Обратите внимание, что, имея «OrderHeader», мы фактически вводим дополнительную концепцию в домен.
источник
Модель вашего домена содержит вашу бизнес-логику в чистом виде. Все отношения и операции, которые поддерживают бизнес-операции. Что вам не хватает в вашей концептуальной карте, так это идея прикладного сервисного уровня, который сервисный слой оборачивает вокруг модели домена и обеспечивает упрощенное представление бизнес-домена (проекция, если хотите), которая позволяет модели домена изменяться по мере необходимости. без непосредственного влияния на приложения, использующие сервисный уровень.
Идти дальше. Идея агрегата состоит в том, что существует один объект, корень агрегата, который отвечает за поддержание согласованности агрегата. В вашем примере заказ будет отвечать за манипуляции со строками заказа.
В вашем примере сервисный уровень предоставит такую операцию, как GetOrdersForCustomer, которая будет возвращать только то, что необходимо для просмотра сводного списка заказов (как вы их называете OrderHeaders).
Наконец, шаблон Repository не просто коллекция, но также допускает декларативные запросы. В C # вы можете использовать LINQ в качестве объекта запроса , или большинство других O / RM также предоставляют спецификацию объекта запроса.
Видя, что вы можете создавать запросы к хранилищу, также имеет смысл предоставить удобные методы, которые обрабатывают общие запросы. Т.е., если вам нужны заголовки вашего заказа, вы можете создать запрос, который возвращает только заголовок, и предоставить его из удобного метода в ваших репозиториях.
Надеюсь, это поможет прояснить ситуацию.
источник
Я знаю, что это старый вопрос, но я, похоже, пришел к другому ответу.
Когда я делаю репозиторий, он обычно включает некоторые кэшированные запросы.
Храните эти репозитории в своих серверах. Они не просто передают объекты в базу данных!
Если я нахожусь в веб-приложении со страницей со списком заказов, которую вы можете щелкнуть, чтобы увидеть подробности, скорее всего, я хочу, чтобы на моей странице списка заказов была подробная информация о заказах (идентификатор, имя, сумма, дата) чтобы помочь пользователю решить, на кого он хочет посмотреть.
На данный момент у вас есть два варианта.
Вы можете запросить базу данных и получить именно то, что вам нужно для включения в листинг, а затем запросить еще раз, чтобы получить отдельные детали, которые вам нужно увидеть на странице сведений.
Вы можете сделать 1 запрос, который извлекает всю информацию и кэширует ее. На следующей странице запроса вы читаете с сервера оперативной памяти вместо базы данных. Если пользователь нажимает кнопку назад или выбирает следующую страницу, вы все равно не обращаетесь к базе данных.
На самом деле, как вы реализуете, это просто, и детали реализации. Если у моего крупнейшего пользователя 10 заказов, я, вероятно, захочу перейти к варианту 2. Если я говорю 10 000 заказов, тогда нужен вариант 1. В обоих вышеупомянутых случаях и во многих других случаях я хочу, чтобы хранилище скрывало детали реализации.
Если я получу билет, чтобы сообщить пользователю, сколько он потратил на заказы ( сводные данные) ) за последний месяц на странице списка заказов, я бы лучше написал логику для вычисления этого в SQL и совершил бы еще одну обратную поездку к БД или вы бы предпочли рассчитать его, используя данные, которые уже находятся в оперативной памяти серверов?
По моему опыту доменные агрегаты предлагают огромные преимущества.
источник