我想知道使用贫血领域模型的优缺点(见下面的链接).
福勒文章
由于"贫血领域模型"是反模式,为什么有这么多系统实现这一点?
我认为有几个原因
1.系统的复杂性
在一个简单的系统(几乎所有的例子和你在互联网上找到的示例代码),如果我想实现:
将产品添加到订单
我把这个功能放在了订单上
public void Order.AddOrderLine(Product product) { OrderLines.Add(new OrderLine(product)); }
很好,超级面向对象.
现在让我们说我需要确保我需要验证产品是否存在于库存中,如果不存在则抛出异常.
我不能再把它放在订单上,因为我不希望我的订单依赖于库存,所以现在它需要继续服务
public void OrderService.AddOrderLine(Order order, Product product) { if (!InventoryService.Has(product) throw new AddProductException order.AddOrderLine(product); }
我也可以将IInventoryService传递给Order.AddOrderLine,这是另一种选择,但仍然使Order依赖于InventoryService.
Order.AddOrderLine中仍然存在一些功能,但通常仅限于订单范围,而根据我的经验,还有更多的业务逻辑无序范围.
当系统不仅仅是基本的CRUD时,您将在OrderService中获得大部分逻辑,而在Order中则很少.
2.开发人员对OOP的看法
互联网上有很多激烈的讨论,关于哪些逻辑应该在实体上进行.
就像是
Order.Save
秩序应该知道如何自救吗?假设我们有存储库.
现在可以订购添加订单行吗?如果我试着用简单的英语来理解它,它也没有意义.用户将产品添加到订单中,那么我们应该使用User.AddOrderLineToOrder()吗?这似乎有点矫枉过正.
OrderService.AddOrderLine()怎么样?现在它有点意义!
我对OOP的理解是,对于封装,你将函数放在函数需要访问类的内部状态的类上.如果我需要访问Order.OrderLines集合,我将Order.AddOrderLine()放在Order上.这样,类的内部状态不会暴露.
3. IoC容器
使用IoC容器的系统通常是完全贫血的.
这是因为您可以测试具有接口的服务/存储库,但不能(轻松地)测试域对象,除非您将接口放在所有接口上.
由于"IoC"目前被称为解决所有编程问题的解决方案,因此很多人盲目地遵循它,这样最终会出现Anemic Domain Models.
OOP很难,程序很容易
我对此有一点" 知识诅咒 ",但我发现对于拥有DTO和服务的新开发者来说比Rich Domain容易得多.
可能是因为使用Rich Domain,更难以知道哪些类可以放置逻辑.何时创建新类?使用哪种模式?等等..
使用无状态服务,您只需在具有最接近名称的服务中使用它.
专业人士:
您可以声称它是一个域模型并吹嘘给您的开发人员朋友并将其放在简历上.
从数据库表中自动生成很容易.
它令人惊讶地映射到数据传输对象.
缺点:
您的域逻辑存在于其他地方,可能在一个充满类(静态)方法的类中.或者您的GUI代码.或者在多个地方,都有相互冲突的逻辑.
这是一种反模式,因此其他开发人员会问您是否理解面向对象设计的概念.
在此之后,我脑子里想了很长时间.我相信"OOP"这个词的含义并不是真正意义上的.正如我们所熟知的那样,anagram意味着"面向对象的编程".当然,重点是"定向"一词.它不是"OMP",意思是"对象强制编程".ADM和RDM都是OOP的例子.它们使用对象,属性,方法接口等.但是,ADM和RDM在我们选择如何封装事物方面存在差异.他们是两个不同的东西.要说ADM是坏的OOP并不是一个准确的说法.也许我们需要不同的术语来代替各种级别的封装.另外,我从不喜欢反模式这个词.它通常由对立组的成员分配给某事物.ADM和RDM都是有效的模式,它们简单地考虑了不同的目标,旨在解决不同的业务需求.我们这些练习DDD的人至少应该理解这一点,而不是通过抨击那些选择实施ADM的人来达到其他人的水平.只是我的想法.
"这是一种反模式,所以其他开发人员会问你是否理解面向对象设计的概念."
"贫血领域模式是一种反模式.反模式没有优势."
贫血领域模型是否是一种反模式是一个观点问题.Martin Fowler表示,许多开发人员都知道内部OO说它不是.将观点陈述为事实很少有用.
一个,即使它被普遍认为是一种反模式,它仍然有一些(虽然相对较小)的上升空间.
在我看来,福勒的主要反对意见是ADM在以下意义上不是OO.如果设计一个系统"从头开始"围绕被其他代码片段操纵的被动数据结构,那么这肯定闻起来像程序设计而不是面向对象的设计.
我建议至少有两种力量可以产生这种设计:
设计师/程序员仍然认为在程序上需要在面向对象的环境中工作(或假设他们可以......)来生成新系统,以及
开发人员致力于将服务式"面孔"放在以非OO方式设计的遗留系统上(无论语言如何).
例如,如果构建一组服务来公开现有COBOL大型机应用程序的功能,则可以根据不镜像内部COBOL数据结构的概念模型来定义服务和接口.但是,如果服务将新模型映射到遗留数据以使用现有但隐藏的实现,那么在Fowler的文章意义上,新模型很可能是"贫血" - 例如一组TransferObject风格的定义和没有真实行为的关系.
对于理想上纯的OO系统必须与现有的非OO环境进行交互的边界,这种折衷可能很常见.
如果您的团队无法或不愿意构建富域模型(RDM)并随着时间的推移维护它,那么贫血域模型(ADM)可能是一个不错的选择.使用RDM获胜需要仔细关注系统中使用的主要抽象.图中,在任何开发组中,不超过一半,也许只有十分之一的成员能够胜任抽象.除非这个干部(可能只有一个开发人员)能够保持对整个集团活动的影响,否则RDM将屈从于熵.
特别是熵RDM会受到伤害.它的开发人员将学习苛刻的课程.起初,他们将能够满足利益相关者的期望,因为他们没有历史可以实现.但随着他们的系统变得更加复杂(不复杂),它将变得脆弱; 开发人员将尝试重用代码,但往往会在开发中引发新的错误或回溯(从而超出他们的估计).
相比之下,ADM开发人员会为自己设定较低的期望,因为他们不希望重用新功能的代码.随着时间的推移,他们将拥有一个存在许多不一致的系统,但它可能不会无法突破.他们的上市时间比成功的RDM更长,但他们的利益相关者不太可能认识到这种可能性.
"开发人员正致力于将服务式"面对"用于以非OO方式设计的遗留系统(无论语言如何)."
如果您考虑许多LOB应用程序,这些遗留系统通常不会使用与您相同的域模型.Anemic Domain Model通过在服务类中使用业务逻辑来解决这个问题.您可以将所有这些接口代码放在模型中(在传统的OO意义上) - 但您通常最终会失去模块化.