доступ к данным в DDD?

После прочтения книг Эвана и Нильссона я все еще не уверен, как управлять доступом к данным в проекте, управляемом доменом. Должны ли методы CRUD быть частью репозиториев, т. е. OrderRepository.GetOrdersByCustomer(customer), или они должны быть частью сущностей: Customer.GetOrders(). Последний подход кажется более объектно-ориентированным, но он будет распределять доступ к данным для одного типа сущности между несколькими объектами, т. е. Customer.GetOrders(), Invoice.GetOrders(), ShipmentBatch.GetOrders() и т. д. Как насчет вставки и обновления?


person Manu    schedule 16.09.2008    source источник


Ответы (6)


CRUD-методы должны быть частью репозитория... Но я думаю, вы должны спросить, почему у вас есть куча методов CRUD. Что они действительно делают? Для чего они на самом деле? Если вы на самом деле вызываете шаблоны доступа к данным, используемые вашим приложением, я думаю, что это делает репозиторий намного более полезным и избавляет вас от необходимости делать дробовики, когда в вашем домене происходят определенные типы изменений.

CustomerRepo.GetThoseWhoHaventPaidTheirBill()

// or

GetCustomer(new HaventPaidBillSpecification())

// is better than

foreach (var customer in GetCustomer()) {
    /* logic leaking all over the floor */
}

Методы типа «Сохранить» также должны быть частью репозитория.

Если у вас есть агрегированные корни, это убережет вас от взрыва репозитория или распространения логики повсюду: у вас нет 4 x # шаблонов доступа к данным сущностей, только те, которые вы фактически используете в агрегированных корнях.

Это мои 0,02 доллара.

person Chris Bilson    schedule 16.09.2008

DDD обычно предпочитает шаблон репозитория шаблону активной записи, на который вы намекаете с помощью Customer.Save.

Недостатком модели Active Record является то, что она в значительной степени предполагает единую модель сохраняемости, за исключением особенно навязчивого кода (в большинстве языков).

Интерфейс репозитория определен на уровне предметной области, но он не знает, хранятся ли ваши данные в базе данных или нет. С помощью шаблона репозитория я могу создать InMemoryRepository, чтобы я мог изолированно тестировать логику домена, и использовать внедрение зависимостей в приложении, чтобы, например, уровень обслуживания создавал экземпляр SqlRepository.

Для многих людей иметь специальный репозиторий только для тестирования звучит глупо, но если вы используете модель репозитория, вы можете обнаружить, что вам на самом деле не нужна база данных для вашего конкретного приложения; иногда простой FileRepository поможет. Связывание себя с базой данных, прежде чем вы узнаете, что она вам нужна, потенциально ограничивает. Даже если база данных необходима, намного быстрее запускать тесты с InMemoryRepository.

Если у вас не так много логики предметной области, вам, вероятно, не нужен DDD. ActiveRecord вполне подходит для многих задач, особенно если у вас в основном данные и немного логики.

person JasonTrue    schedule 16.09.2008

Отступим на секунду. Эванс рекомендует, чтобы репозитории возвращали сводные корни, а не только сущности. Итак, если предположить, что ваш клиент является совокупным корнем, включающим заказы, то, когда вы извлекаете клиента из его репозитория, заказы приходят вместе с ним. Вы можете получить доступ к заказам, перейдя от отношения «Клиент» к «Заказы».

customer.Orders;

Итак, чтобы ответить на ваш вопрос, операции CRUD присутствуют в совокупных корневых репозиториях.

CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);
person Stefan Moser    schedule 20.09.2008

Я сделал это обоими способами, о которых вы говорите. Сейчас я предпочитаю метод постоянного невежества (или PONO - Plain Ole '.Net Object), когда ваши классы домена беспокоятся только о том, чтобы быть классами домена. Они ничего не знают о том, как они настойчивы, и даже если они настойчивы. Конечно, вы должны время от времени относиться к этому прагматично и учитывать такие вещи, как идентификатор (но даже в этом случае я просто использую супертип слоя, который имеет идентификатор, поэтому у меня может быть единственная точка, где живут такие вещи, как значение по умолчанию)

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

Одна вещь, на которую следует обратить внимание, — это раздувание методов, от которого могут страдать репозитории. GetOrderbyCustomer.. GetAllOrders.. GetOrders30DaysOld.. и т. д. и т. д. Одним из хороших решений этой проблемы является рассмотрение шаблона Query Object. И тогда ваши репозитории могут просто принять объект запроса для выполнения.

Я также настоятельно рекомендую изучить что-то вроде NHibernate. Он включает в себя множество концепций, которые делают репозитории такими полезными (карта удостоверений, кэш, объекты запросов и т. д.).

person Shane Courtrille    schedule 16.09.2008

Даже в DDD я бы разделил классы и подпрограммы доступа к данным от сущностей.

Причины,

  1. Тестируемость улучшается
  2. Разделение задач и модульная конструкция
  3. Более удобный в обслуживании в долгосрочной перспективе, поскольку вы добавляете сущности, подпрограммы

Я не эксперт, просто мое мнение.

person Vin    schedule 16.09.2008

Раздражает то, что в «Применении DDD&P» Нильссона он всегда начинает со слов «Я бы не стал этого делать в реальном приложении, но…», а затем следует его пример. Вернемся к теме: я думаю, что OrderRepository.GetOrdersByCustomer(customer) — это правильный путь, но также есть обсуждение в списке рассылки ALT.Net (http://tech.groups.yahoo.com/group/altdotnet/) о DDD.

person Community    schedule 16.09.2008