Методы обработки аномальной модели домена
Я прочитал некоторые из вопросов, касающихся моделей анемичных доменов и разделения проблем. Каковы наилучшие методы для выполнения/присоединения логики домена на объектах аномальной области? На моей работе у нас довольно анемичная модель, и в настоящее время мы используем "вспомогательные" классы для выполнения базы данных/бизнес-логики на объектах домена. Например:
public class Customer
{
public string Name {get;set;}
public string Address {get;set;}
}
public class Product
{
public string Name {get;set;}
public decimal Price {get;set;}
}
public class StoreHelper
{
public void PurchaseProduct(Customer c, Product p)
{
// Lookup Customer and Product in db
// Create records for purchase
// etc.
}
}
Когда приложение должно совершить покупку, оно создаст StoreHelper и вызовет метод на объектах домена. Для меня было бы разумным, чтобы Клиент/Продукт знал, как сохранить себя в репозитории, но вам, вероятно, не нужны методы Save() для объектов домена. Это также имеет смысл для метода, такого как Customer.Purchase(Product), но это ставит доменную логику на объект.
Вот некоторые методы, с которыми я столкнулся, не уверены, какие из них хорошие/плохие:
- Клиент и продукт наследуются от класса "Entity", который предоставляет основные операции CRUD общим способом (возможно, с использованием ORM).
- Плюсы: каждый объект данных автоматически получает операции CRUD, а затем привязывается к базе данных /ORM
- Минусы: это не решает проблему бизнес-операций над объектами, а также связывает все объекты домена с базовым объектом, который может быть непригоден.
- Используйте вспомогательные классы для обработки операций CRUD и бизнес-логики
- Имеет ли смысл иметь DAO для операций "чистой базы данных" и отдельных бизнес-помощников для более конкретных бизнес-операций?
- Для этого лучше использовать нестатические или статические вспомогательные классы?
- Плюсы: объекты домена не привязаны к какой-либо базе данных/бизнес-логике (полностью анемии)
- Минусы: не очень OO, не очень естественно использовать помощников в коде приложения (выглядит как код C)
- Используйте метод Double Dispatch, где у объекта есть методы для сохранения в произвольном хранилище
- Плюсы: лучшее разделение проблем
- Минусы: объекты имеют некоторую дополнительную логическую привязку (хотя она развязана)
- В С# 3.0 вы можете использовать методы расширения для присоединения CRUD/бизнес-методов к объекту домена, не касаясь его
- Это действительный подход? Какие плюсы и минусы?
- Другие методы?
Каковы наилучшие методы обработки этого? Я новичок в DDD (я читаю книгу Эванса - возможно, это откроет мне глаза)
Ответы
Ответ 1
Маркус Фаулер много писал о моделях доменов, в том числе моделях анонимных доменов. Он также имеет краткие описания (и диаграммы классов UML) многих шаблонов проектирования для моделей и баз данных домена, которые могут быть полезны: Каталог "Шаблоны архитектуры корпоративных приложений" .
Я бы предложил посмотреть Active Record и Data Mapper шаблоны. Из описания вашей проблемы звучит так, что ваши вспомогательные классы содержат как правила домена/бизнеса, так и данные о реализации базы данных.
Активная запись переместила бы логику вспомогательного домена и код базы данных в другие объекты домена (например, ваш базовый класс Entity
). Data Mapper переместил бы логику вспомогательного домена в объекты домена и код базы данных в отдельный объект карты. Любой подход был бы более объектно-ориентированным, чем вспомогательные классы процедурного стиля.
Книга Эрика Эванса "Домен Driven Design" превосходна. Он становится немного сухим, но определенно стоит того. В InfoQ есть мини-книга с мини-книгой, разработанная под управлением домена, которая является хорошим введением в книгу Эванса. Плюс "Быстрое создание кода домена" доступно в виде бесплатного PDF файла.
Ответ 2
Чтобы избежать анемичной модели, реорганизовать свои вспомогательные классы:
Логика вроде:
"Customer.PurchaseProduct(продукт продукта, оплата платежа)",
"Customer.KillCustomer(Человек-убийца, Оружие оружия)"
должны существовать прямо в объекте домена "Клиент".
Логика вроде:
"Customer.IsCustomerAlive()"
"Customer.IsCustomerHappy()"
должны соответствовать спецификациям.
Логика вроде:
"Customer.Create()",
"Customer.Update()"
очевидно, должны идти в хранилища.
Логика вроде:
"Customer.SerializeInXml()"
"Customer.GetSerializedCustomerSizeInBytes()"
следует обратиться к услугам.
Комплексные конструкторы должны идти на заводы.
Что я вижу. Я был бы рад, если бы кто-нибудь мог прокомментировать мое понимание подхода DDD.
Edit:
Иногда - не следует избегать анемичной модели домена .
Отредактировал мой ответ, чтобы добавить, что DDD - это не собирание и удаление шаблонов.
DDD о том, как мы думаем.
Ответ 3
Я всегда думал о модели анемичного домена как анти-шаблоне. Это ясно, что клиент будет покупать продукты, что способность может быть создана с помощью реализации интерфейса
Interface IPurchase
Purchase(Product);
поэтому любой из ваших объектов домена может затем реализовать это, как требуется. Таким образом, вы можете ввести функциональность для своих объектов домена - именно там, где это должно быть.
Ответ 4
Один из подходов, который вы не упомянули, использует AOP для обработки вашего доступа к данным. Пример моего недавнего использования этого подхода (хотя и значительно упрощенного для целей публикации) состоял в том, что у меня был объект домена Account
, который имел метод debit
, инкапсулируя бизнес-логику, необходимую для успешного дебетования из учетной записи.
N.B. Весь код - это Java с нотации AspectJ AOP...
public boolean debit(int amount) {
if (balance - amount >= 0) {
balance = balance - amount;
return true;
}
return false;
}
С соответствующим репозиторием, введенным в мой аспект, я затем использовал pointcut для перехвата вызовов к этому методу...
pointcut debit(Account account,int amount) :
execution(boolean Account.debit(int)) &&
args(amount) &&
target(account);
... и применил несколько советов:
after(Account account, int amount) returning (boolean result) : debit(account,amount) {
if (result) getAccountRepository().debit(account, amount);
}
По-моему, это дает прекрасное разделение проблем и позволяет вашим субъектам домена полностью сосредоточиться на бизнес-логике вашего приложения.