Domain driven design 贫血领域模型的处理技术
我已经阅读了一些关于贫血领域模型和关注点分离的问题。在贫血的域对象上执行/附加域逻辑的最佳技术是什么?在我的工作中,我们有一个相当贫乏的模型,我们目前正在使用“helper”类在域对象上执行数据库/业务逻辑。例如:Domain driven design 贫血领域模型的处理技术,domain-driven-design,business-logic,Domain Driven Design,Business Logic,我已经阅读了一些关于贫血领域模型和关注点分离的问题。在贫血的域对象上执行/附加域逻辑的最佳技术是什么?在我的工作中,我们有一个相当贫乏的模型,我们目前正在使用“helper”类在域对象上执行数据库/业务逻辑。例如: public class Customer { public string Name {get;set;} public string Address {get;set;} } public class Product { public string Nam
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)这样的方法也有意义,但这是将域逻辑放在实体上
以下是我遇到的一些技巧,不确定哪些是好的/坏的:
- 优点:每个数据对象都会自动获取CRUD操作,但随后会绑定到数据库/ORM
- 缺点:这并不能解决对象上的业务操作问题,还将所有域对象绑定到可能不合适的基础实体
- 对于“纯数据库”操作使用DAO,对于更具体的业务操作使用单独的业务助手,这有意义吗
- 使用非静态或静态帮助器类更好吗
- 优点:域对象不与任何数据库/业务逻辑相关联(完全贫乏)
- 缺点:不是很面向对象,在应用程序代码中使用助手也不是很自然(看起来像C代码)
- 优点:更好地分离关注点
- 缺点:实体附加了一些额外的逻辑(尽管它是解耦的)
- 这是一种有效的方法吗?赞成/反对意见是什么
处理此问题的最佳技术是什么?我是DDD的新手(我正在读埃文斯的书——也许这会让我大开眼界)我一直认为贫血领域模型是一种反模式。很明显,客户会购买产品,这种能力可以通过接口实现来实现
Interface IPurchase
Purchase(Product);
,这样您的任何域对象都可以根据需要实现它。通过这种方式,您可以向域对象引入功能,而这正是它应该具备的功能。Martin Fowler已经写了很多关于域模型的文章,包括。他还简要描述了领域模型和数据库的许多设计模式(以及UML类图),这些设计模式可能会有所帮助: 我建议看一下这些规则和模式。从问题的描述来看,助手类似乎同时包含域/业务规则和数据库实现细节 活动记录将帮助者的域逻辑和数据库代码移动到其他域对象中(如
实体基类)。数据映射器将助手的域逻辑移动到域对象中,将数据库代码移动到单独的映射对象中。这两种方法都比过程式助手类更面向对象
Eric Evans的《领域驱动设计》一书非常优秀。虽然有点干,但绝对值得。InfoQ有一个很好的例子来介绍埃文斯的书。另外,“域驱动的快速设计”是一个免费的PDF格式。为了避免贫血模型,重构您的助手类:
逻辑如下:
“客户购买产品(产品、付款)”,
“顾客。杀手顾客(人杀手、武器)”
应直接存在于“客户”域对象中
逻辑如下:
“Customer.IsCustomerAlive()”
“Customer.IsCustomerHappy()”
应该符合规格
逻辑如下:
“Customer.Create()”,
“Customer.Update()”
显然,我们应该去存储库
逻辑如下:
“Customer.SerializeInXml()”
“Customer.GetSerializedCustomerSizeInBytes()”
应该去服务中心
复杂的施工人员应该去工厂
我就是这么看的。如果有人能评论我对DDD方法的理解,我将非常高兴
编辑:
有时-贫血区域模型
编辑了我的答案,添加了DDD与拾取和丢弃模式无关的内容。
DDD是我们思考的方式。您没有提到的一种方法是使用AOP来处理数据访问。我最近使用这种方法的一个例子(尽管为了过账目的大大简化了)是我有一个账户
域实体,它有一个借记
方法,封装了成功从账户进行借记所需的业务逻辑
注意:所有代码都是带有AspectJ AOP符号的Java
将适当的存储库注入我的方面后,我使用切入点拦截对该方法的调用
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);
}
在我看来,这可以很好地分离关注点,并允许您的域实体完全关注应用程序的业务逻辑。似乎有很多不同的类只是为了处理客户。为什么不把它的大部分放在一个类中,用一个服务来处理任何复杂的事情呢?我的答案非常老套D@LuckyLindy主要是因为DDD是关于在领域专家和程序员之间建立桥梁的。域模型不应该包含技术内容,否则泛在语言将无法存在。为了转移技术性的东西,我们必须把它抽象出来。抽象某些东西总是会膨胀代码库
after(Account account, int amount) returning (boolean result) : debit(account,amount) {
if (result) getAccountRepository().debit(account, amount);
}