|
|
1
135
“贫血领域模型”是反模式的,为什么有这么多系统实现了这一点? 我认为有几个原因 1.系统的复杂性
向订单添加产品 我把这个函数放在订单上
很好,超级面向对象。 现在让我们假设我需要确保我需要验证产品是否存在于库存中,如果不存在,则抛出异常。
我还可以将IInventoryService传递给Order.AddOrderLine,这是另一个选项,但这仍然使订单依赖于InventoryService。 Order.AddOrderLine中仍然有一些功能,但通常它仅限于Order范围,而根据我的经验,有很多业务逻辑超出了Order范围。 当系统不仅仅是基本的CRUD时,您将以OrderService中的大部分逻辑和很少的顺序结束。
互联网上有很多关于哪些逻辑应该在实体上进行的激烈讨论。 差不多 命令,保存
现在可以添加订单行了吗?如果我试图用简单的英语来理解它,它也没有真正意义。用户将产品添加到订单中,我们也应该这样做User.AddOrderLineToOrder()?这似乎太过分了。 OrderService.AddOrderLine()怎么样。现在有点道理了! 我对OOP的理解是,对于封装,将函数放在类上,函数需要访问类的内部状态。如果我需要访问Order.OrderLines集合,我会将Order.AddOrderLine()放在Order上。这样类的内部状态就不会暴露。
使用IoC容器的系统通常是完全贫血的。
由于“IoC”目前被誉为解决所有编程问题的解决方案,许多人盲目地遵循它,最终导致领域模型贫乏。
我有一点 Curse of Knowledge “在这个问题上,但我发现 对于较新的开发人员来说,拥有DTO和服务比富域要容易得多。 可能是因为对于富域,更难知道将逻辑放在哪个类上。何时创建新类?使用哪种模式?等 对于无状态服务,您只需使用最接近的名称将其插入服务中。 |
|
|
2
37
优点:
缺点:
|
|
|
3
20
在这之后,我脑子里有一个想法已经很久了。我相信“OOP”一词的含义并不是它的本意。我们都知道,这个字谜的意思是“面向对象编程”。当然,重点是“面向”一词。它不是“OMP”,意思是“对象强制编程”。ADM和RDM都是OOP的例子。它们利用对象、属性、方法和接口等。然而,ADM和RDM在如何选择装箱方面存在差异。它们是两种不同的东西。说ADM是糟糕的OOP并不是一个准确的说法。也许我们需要不同的术语来表示不同的封装级别。此外,我从不喜欢“反模式”这个词。它通常由对立团体的成员分配给某物。ADM和RDM都是有效的模式,它们的目的不同,旨在解决不同的业务需求。我们这些实践DDD的人至少应该认识到这一点,而不是通过抨击那些选择实施ADM的人而达到其他人的水平。这只是我的想法。 |
|
|
4
18
“贫血领域模型是一种反模式。反模式没有好处。” 贫血领域模型是否是一种反模式是一个意见问题。MartinFowler说是的,许多从内到外了解OO的开发人员说不是。 把意见说成事实很少有帮助。 然而,即使它被普遍认为是一种反模式,它仍有一些(尽管相对较小)的上升空间。 |
|
|
5
13
我建议至少有两种力可以产生这种设计:
例如,如果您正在构建一组服务来公开现有COBOL大型机应用程序的功能,那么您可以根据一个概念模型来定义服务和接口 不
|
|
|
6
8
如果您的团队无法或不愿意构建富域模型(RDM)并长期维护,贫血域模型(ADM)可能是一个不错的选择。要想赢得RDM,需要仔细注意系统中使用的主要抽象。请注意,在任何开发团队中,不超过一半的成员,也许只有十分之一的成员能够胜任抽象工作。除非这个干部(可能只有一个开发人员)能够对整个团队的活动保持影响力,否则RDM将屈从于熵。 熵RDM的伤害尤其严重。它的开发者将从中吸取惨痛的教训。首先,他们将能够满足利益相关者的期望,因为他们将没有任何历史可以兑现。但随着他们的系统变得越来越强大 complicated (not complex) 它会变脆;开发人员将尝试重用代码,但往往会在开发过程中引发新的错误或倒退(从而超出他们的估计)。 相比之下,ADM开发人员对自己的期望较低,因为他们不希望为新功能重用那么多代码。随着时间的推移,他们将拥有一个有许多不一致之处的系统,但它可能不会无一例外地崩溃。他们的上市时间将比成功的RDM更长,但他们的利益相关者不太可能意识到这种可能性。 |
|
|
7
5
如果您想到许多LOB应用程序,这些遗留系统通常不会使用与您相同的域模型。贫血域模型通过在服务类中使用业务逻辑来解决这个问题。您可以将所有这些接口代码放在您的模型中(在传统的OO意义上),但最终通常会失去模块性。 |
|
|
8
4
当我第一次看到贫血领域模型文章时,我想“神圣的s***,这就是我所做的。恐怖!”我坚持下去,并参考了Eric Evan的书,认为这是一个很好的例子,并下载了源代码。事实证明,“不使用贫乏的域模型”并不意味着“不使用服务类、不使用中介、不使用策略”,甚至“将逻辑放在被操纵的类上”。 DDD示例有服务类、XyzUpdaters、Singleton和IoC。 我仍然对贫血领域模型的确切含义感到困惑。我希望“我看到它就会知道”。现在我满足于一个好设计的正面例子。 |
|
|
9
4
我与一个“成熟”的ADM系统一起工作过,我觉得我可以对这个问题提供一些,至少是轶事性的反馈。 1) 缺乏封装 在带有ADM的实时系统中,可以写入例如“obj.x=100;保存”,即使这违反了业务逻辑。这会导致一些错误,如果不变量是在对象上建模的话,这些错误是不会遇到的。我觉得这些缺陷的严重性和普遍性对ADM来说是最严重的负面影响。 我认为有必要在此指出,这是功能性解决方案与ADM程序性解决方案的显著不同之处,其他人可能会得出的任何相似之处ADM与功能性解决方案之间的表面相似之处都是偶然的。 2) 代码膨胀
3) 对领域问题理解不足
4) 维修难度 考虑到域概念可能不仅仅是复制粘贴的重新实现,因此需要一定程度的严格性来确保域概念在其表达的所有位置都发生了更改。这通常会导致在多个场合对相同的bug进行调查和修复。 5) 增加了登机的难度 我认为RDM的好处之一是概念的内聚性,它允许更快地理解领域。有了ADM,概念可能会支离破碎,缺乏清晰性,因此新开发人员更难获得。 我还试图将ADM的运营支持成本包括在RDM的运营支持成本中,但这取决于许多因素。 正如其他人所指出的,看看DDD(Greg Evans、Vince Vaughn和Scott Millett)来了解RDM的好处。 |
|
|
10
2
这和大多数反模式的做法是一样的:它可以让很多人长时间忙碌。当管理者管理更多的人时,他们往往会得到更多的报酬,因此存在着不改进的强烈动机。 |
|
|
11
2
与Eric P的回答以及上面其他一些人所写的一致,ADM的主要缺点似乎是缺少OOD,特别是将域概念的逻辑和数据保持在一起,以便在API丰富的同时隐藏实现细节。 Eric接着指出,在域类之外通常有一些信息是对该类进行操作的逻辑所必需的,例如在向订单添加项目之前检查库存。不过,我质疑的是,答案是持有这种总体逻辑的服务层,还是作为对象设计的一部分更好地处理它。 某人 必须了解库存对象、产品对象和订单对象。也许它只是一个OrderSystem对象,它有一个库存成员、一个订单列表等等。这看起来与服务没有太大区别,但我认为它在概念上更加连贯。 或者这样看:你可以有一个拥有内部信用余额的用户,每次调用User.addItemToOrder(item)时,它都会获取物品的价格,并在添加之前检查信用,等等。这似乎是一个合理的OO设计。我不确定用Service.addItemToUserOrder(用户,项目)替换它会损失什么,但我也不确定会得到什么。我想损失将是额外的代码层,加上更笨拙的写作风格和对底层域模型的强制忽视。 |
|
12
1
应该注意的是,随着系统复杂性和变化粒度的增加,设计良好的消息传递对象模型所提供的接口点的封装和整合使得在不进行广泛重构的情况下更改和维护关键代码更加安全。
我还想补充一点,并非所有情况都需要域模型(更不用说ADM模型了)。有时,最好使用程序化/功能性更强的任务样式,因为任务是数据驱动的,不依赖于应用程序范围的逻辑/业务规则。
还要提前考虑哪一个更容易维护。。。 |
|
|
13
1
它提供了更好的可预测性。经理们喜欢这样,特别是如果项目是按时支付的;材料。每一次改变都意味着大量的工作,所以困难的工作可能隐藏在大量重复性工作的背后。在一个设计良好的干燥系统中,可预测性非常差,因为你总是在做新的事情。 |
|
|
14
1
在学习了更多关于这个主题的知识并使用了战略模式之后,我终于开始理解这一点 这一切都是为了深入了解业务问题 只有在这之后,你才能决定系统的哪些部分适合应用 战术模式 例如 富有的 贫血的 足够地 关于业务逻辑 对于系统的那个部分。 因此,在实施手头问题的解决方案时,首先应该确定是否使用 积垢 基于方法或投资于 富有的 以及上述战术模式。 但是 在这种情况下,不存在所谓的 贫血域模型 根本没有域模型 . 你更愿意看到的是 (数据传输对象)或 数据访问对象 (数据访问对象)和将对数据进行操作的服务类。相应的操作在很大程度上涉及将数据从一种表示形式转换为另一种表示形式,并在几乎没有业务逻辑的情况下移动数据。 许多复杂的业务逻辑 这也会随着时间的推移而改变 根据我的经验,这是个好主意。原因是通过代码更容易表示业务透视图,并且更容易理解反映业务域及其规则的相应操作。这并不意味着在每个用例中都必须有域模型类。例如,如果没有状态可以被改变和持久化,那么也只能有包含域逻辑的域服务被实现得更像纯函数。 但是,如果还有一种状态需要变异和持续,而这种状态在商业领域中也有目的和意义,那么改变这种状态的状态和行为应该是 封装 . 因此,没有人能够轻松绕过业务规则,从而导致无效状态和严重故障。 所谓 贫血的 领域模型通常是此类问题的根源 你失去了很多工作的好处 富有的 在基于DDD的方法中,还存在上述问题。当使用行为及其数据放在同一个类中的域模型时,也可能会有许多不同的“客户机”调用该类的操作,但他们不需要注意业务实体的业务不变量是否得到遵守,因为域模型类将始终注意这一点,并且还可以通知“客户机”关于无效操作,甚至抛出异常作为安全网。 所以 要旨 不要将数据结构类(如DTO或DAO)与贫乏的域模型类混淆 . 在精心选择的基于CRUD的方法中,尝试使用域模型没有任何好处,因为业务逻辑太不复杂了。 通过 贫血域模型 我会参考代码,从中我可以看到,有许多复杂的业务逻辑和业务规则分布在不同的组件中,这些组件应该更接近这些逻辑正在更改的数据。 在此过程中,我还学到了另一个教训:如果您尝试使用相同的商业语言(也称为 |
|
|
15
0
从我的观点来看,域的关键是它必须保持简单的测试行为
目标 对于一个拥有尽可能多的商业行为的DM,将其余的放在明确指定的区域(即不在服务中) |
|
|
16
0
在RDM之上使用ADM的好处可以从如何将对象持久化到db中看出。在遗留代码系统上工作的开发人员可以使用我们的业务对象(来自新系统),并继续使用他们当前的数据访问层将这些对象持久化到数据库中。使用RDM将迫使遗留系统的开发人员将存储库对象注入到我们的业务模型中……这与他们当前的数据访问层不一致。 |
|
|
17
-16
anemic domain model 这是一种反模式。反模式没有好处。 |