После чтения книг Эвана и Нильсона я все еще не уверен, как управлять доступом к данным в проекте, управляемом доменом. Если методы CRUD являются частью репозиториев, то есть OrderRepository.GetOrdersByCustomer(клиент) или должны быть частью сущностей: Customer.GetOrders(). Последний подход кажется более OO, но он будет распространять Data Access для одного типа сущности среди множества объектов, то есть Customer.GetOrders(), Invoice.GetOrders(), ShipmentBatch.GetOrders() и т.д. Что касается вставки и обновления?
Доступ к данным в DDD?
Ответ 1
CRUD-ish методы должны быть частью репозитория... ish. Но я думаю, вы должны спросить, почему у вас есть куча методов CRUD. Что они на самом деле делают? Для чего они на самом деле? Если вы на самом деле обращаетесь к шаблонам доступа к данным, используемым вашим приложением, я думаю, что это делает репозиторий намного более полезным и не дает вам делать операцию с дробовиком, когда с вашим доменом происходят определенные изменения.
CustomerRepo.GetThoseWhoHaventPaidTheirBill()
// or
GetCustomer(new HaventPaidBillSpecification())
// is better than
foreach (var customer in GetCustomer()) {
/* logic leaking all over the floor */
}
Методы типа "Сохранить" также должны быть частью репозитория.
Если у вас есть сводные корни, это не дает вам взрыва в репозитории или имеет разбросанную логику: у вас нет 4 x # сущностей для доступа к данным, только те, которые вы используете на совокупных корнях.
Что мои $.02.
Ответ 2
Обычно DDD предпочитает шаблон репозитория по активному шаблону записи, который вы намекаете с помощью Customer.Save.
Один из недостатков в модели Active Record заключается в том, что он в значительной степени предполагает одну модель сохранения, запрещающую некоторый особенно навязчивый код (на большинстве языков).
Интерфейс репозитория определен на уровне домена, но не знает, хранятся ли ваши данные в базе данных или нет. С помощью шаблона репозитория я могу создать InMemoryRepository, чтобы я мог изолировать логику домена и использовать инъекцию зависимостей в приложении, чтобы, например, создать слой службы, например, SqlRepository.
Для многих людей наличие специального репозитория для тестирования звучит глупо, но если вы используете модель репозитория, вы можете обнаружить, что вам действительно не нужна база данных для вашего конкретного приложения; иногда простой FileRepository будет делать трюк. Свадьба себе в базу данных, прежде чем вы знаете, что вам нужно, это потенциально ограничивает. Даже если необходима база данных, гораздо быстрее запускать тесты против InMemoryRepository.
Если у вас не так много на пути логики домена, вам, вероятно, не нужен DDD. ActiveRecord вполне подходит для множества проблем, особенно если у вас в основном есть данные и немного логики.
Ответ 3
Позвольте отступить на секунду. Evans рекомендует, чтобы репозитории возвращали агрегированные корни, а не только объекты. Поэтому, полагая, что ваш Клиент является агрегированным корнем, который включает в себя Заказы, тогда, когда вы вытащили клиента из своего репозитория, приказали вместе с ним. Вы можете получить доступ к заказам, перемещая отношения от клиента к заказу.
customer.Orders;
Итак, чтобы ответить на ваш вопрос, CRUD-операции присутствуют в совокупных корневых репозиториях.
CustomerRepository.Add(customer);
CustomerRepository.Get(customerID);
CustomerRepository.Save(customer);
CustomerRepository.Delete(customer);
Ответ 4
Я сделал это в обоих направлениях, о которых вы говорите. Мой предпочтительный подход теперь - это постоянный неосведомленный (или PONO-Plain Ole..NET Object) метод, в котором ваши классы домена беспокоятся только о том, являются ли классы домена. Они ничего не знают о том, как они сохраняются или даже если они сохраняются. Конечно, вы должны время от времени прагматично относиться к этому вопросу и допускать такие вещи, как Id (но даже тогда я просто использую тип super super, у которого есть Id, поэтому я могу иметь единственную точку, в которой живут такие вещи, как значение по умолчанию)
Основная причина этого заключается в том, что я стараюсь следовать принципу единоличной ответственности. Следуя этому принципу, я обнаружил, что мой код намного надежнее и удобнее. Кроме того, гораздо легче вносить изменения, когда они нужны, потому что мне нужно только обдумать.
Одной вещью, которую следует соблюдать, является метод раздувания, с которым могут пострадать репозитории. GetOrderbyCustomer.. GetAllOrders.. GetOrders30DaysOld.. и т.д. Одним из хороших решений этой проблемы является просмотр шаблона объекта запроса. И тогда ваши репозитории могут просто взять объект запроса для выполнения.
Я также настоятельно рекомендую посмотреть что-то вроде NHibernate. Он включает в себя множество концепций, которые делают Repositories настолько полезными (Identity Map, Cache, Query objects..)
Ответ 5
Даже в DDD я бы сохранил классы и подпрограммы доступа к данным от Entities.
Причины:
- Возможность тестирования улучшается
- Разделение проблем и модульная конструкция
- Более удобная в долгосрочной перспективе, поскольку вы добавляете объекты, подпрограммы
Я не эксперт, просто мое мнение.
Ответ 6
Досадная вещь с Нильссоном. Применение DDD & P заключается в том, что он всегда начинается с "Я бы этого не делал в реальном приложении, но...", а затем его пример следует. Вернемся к теме: Я думаю, что OrderRepository.GetOrdersByCustomer(клиент) - это путь, но есть также обсуждение в списке рассылки ALT.Net(http://tech.groups.yahoo.com/group/altdotnet/) о DDD.