Вопрос

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

Это было полезно?

Решение

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

CustomerRepo.GetThoseWhoHaventPaidTheirBill()

// or

GetCustomer(new HaventPaidBillSpecification())

// is better than

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

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

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

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

Другие советы

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

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

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

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

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

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

customer.Orders;

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

CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);

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

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

Единственное, на что следует обратить внимание, - это раздувание метода, от которого могут пострадать репозитории.Получите заказ от клиента..Получите заказы..Получите заказы на 30 дней распродажи..и т.д. и т.п.Одним из хороших решений этой проблемы является просмотр шаблона объекта запроса.И тогда ваши репозитории могут просто принять объект запроса для выполнения.

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

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

Причинами являются,

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top