DDD-贫血域模型是反模式吗?我们应该使用富域模型吗?[关闭]


11

Enes 和Fowler很久以前就批评了贫血症领域模型,因为它显然违反了面向对象的原理等。DDD社区显然与此声明保持一致。

但是,近年来,一直有不同意见的人声称它根本不是反模式,并且它是遵循SOLID原则的一个示例。

我已经使用Spring Framework工作了很多年。每个公司中的每个项目都始终使用服务于贫乏模型(JPA实体)的存储库,其中包含业务逻辑的服务层。此外,大多数样本,甚至包括Spring员工的官方样本,都展示了这种工作方式。

我的问题是:贫血领域模型是否仍被视为反模式?我们所有人都在做关于DDD的事情吗?您是否不认为拥有Rich Domain模型违反了SOLID原则?


2
贫血领域模型仍被视为反模式吗? ”。有些人是的。在其他人(包括我本人)中,大多数情况下将其视为编写代码的首选方法,但是如果RDM适合您,请使用它。因此,投票结束这纯粹是基于观点。
David Arno

1
对于讨论/辩论论坛,这将是一个奇妙的问题;但目前尚无权威答案。 我们更喜欢可以回答的问题,而不仅仅是讨论的问题。
VoiceOfUnreason

Answers:


9

ADM是解决诸如微服务之类的分布式服务的好模式。它适合当今许多基于Web的业务案例。

考虑是否有一个Order Domain对象。使用OOP方法,我们可以添加Order.Purchase()Order.Cancel()等。它在桌面应用程序中可以很好地工作,在桌面应用程序中,我们将订单保存在内存中,并对同一个实例执行多项操作。

但是,如果我们有一个分布式系统,并且该程序只处理一件事,即访问一个订单列表并依次购买每个订单,或者获取一个订单列表并依次取消每个订单,那么将两个Method放在同一个对象上将不会感。我们将必须具有两个域或有界上下文:

PurchaseSystemOrder.Purchase()

CancelSystemOrder.Cancel();

这些对象唯一共享的是属性的数据结构。

随着您添加越来越多的微服务,您最终获得了数十种订单。 Order称为Domain对象不再有意义,即使它与所有这些系统正在处理的概念顺序相同。

有一个Anemic模型Order更有意义,该模型仅封装数据并相应地重命名服务:

PurchaseService.Purchase(Order order)

现在,我们可以再次讨论Order,我们可以添加我们认为可以处理的任何新服务,而不会影响其他当前已部署的服务。

Fowler和Co来自整体系统背景,在他们的世界中,ADM方法将意味着在内存中实例化所有这些独立服务并传递和更改OrderDTO的单个应用程序。这比将这些方法置于丰富的Order模型上要糟糕得多。

但是在分布式系统中,有很多程序,每个程序仅需要一个Order方法,并以多个订单运行它,加载每个程序,运行该方法,然后将其丢弃。它只需要一个Service和一个数据对象流。

完全填充一个丰富的模型,担心所有方法的需求和依赖性仅调用一个模型然后几乎立即丢弃该对象是没有意义的。

加上对其中一种方法的更改,将需要更新所有分布式组件,因为它们都依赖于Rich Model的逻辑。

我的代码库中没有空间容纳他们不需要的东西


4
我之所以投票,是因为由于上下文上下文概念的限制,我认为DDD特别适合微服务。我认为答案的问题在于,每个微服务的Order概念和类都相同,但是我认为这既不必要又不准确。
RibaldEddie

我不同意,如果您致电PurchaseService CashRegister并将其作为域语言的一部分,则可以将ADM设为DDD
Ewan

你能详细说明吗?我一直认为ADM和DDD在SOLID J2EE或.NET C#MVC EF代码库中时为YMMV。
RibaldEddie '17

我的意思是,有界上下文Order,PurchaseSystemOrder.Purchase()与CashRegister.Purchase(Order order)几乎是相同的代码,这只是您域语言中的命名更改。丰富的域模型意味着在同一个类上同时具有Purchase和Cancel方法。
伊万(Ewan)

1
@RibaldEddie,就“ javaScript,好的部分”这本书的精髓而言,我提供此图像作为(不完全严肃的)论点反对使用DDD实现微服务;)
David Arno

3

SOLID和DDD彼此正交,这意味着它们实际上彼此之间的方向不同。您不应该说一个习惯于另一个,因为它们可以并且应该一起存在于同一代码库中。

只有当您的问题域有很多行为并且您有数百种带有复制粘贴逻辑或依赖项的服务方法时,贫血域模型才成为反模式。服务层方法必须调用其他服务层方法才能完成任何事情。

由于有限的上下文概念,DDD是微服务的绝佳范例。

注意将基础结构代码视为您的域的一部分的诱惑。基础结构代码是您自己没有编写的任何东西,或者是与与第三方系统交互的框架或库耦合的任何东西。数据库连接,SMTP邮件程序,ORM库,应用程序服务器运行时间,所有这些都是基础架构,如果可以避免的话,您不应在域中包括或依赖它。

SOLID原则是一组通用的OOP概念,可用于编写更好的OOP代码。您可以使用它们编写良好的DDD代码库。


关于基础结构代码与域的分离。到目前为止,我已经将JPA实体视为我的领域,具有以下应用程序层:控制器->服务->存储库->域(JPA实体)。根据您的观点,对它进行建模的正确方法(或正确方法之一)是什么?像这样:控制器->丰富模型-> x?我将如何以及在哪里处理交易性?JPA映射又如何呢?我们是否不必使用分离的JPA实体来复制我们的丰富模型?
依赖

JPA实体是一个普通的旧Java对象。映射是基础结构的一部分,但类本身不是必须的。如果选择将其视为基础结构的一部分,则应将其视为工厂或存储库中用于创建域实体的数据传输对象。
RibaldEddie '17

1

如果做得好,丰富的域名会很好。不做噩梦。贫血域总是不好的。但是它熟悉的舒适有点不好。

如果仅是贫血域,则在选择通用语言时会选择错误的语言。第四代语言专门针对特定用途。如果贫血病足够好,使用它们会让您的生活更轻松。

许多框架会进入语言空间,直到您不再将其作为Java作业来宣传。这是Java / Spring工作。他们所做的,除了使您依赖于他们之外,还将通用语言转变为第四代语言的混蛋形式。

这是最佳实践还是反模式?好吧,您的老板大多只是在乎您是否可以聘请人员在代码库上工作。因此,如果我们必须假装我们正在使用通用语言来招聘人员,我们会的。

如果可以的话就可以了。您当然不是唯一的一个。

但是不要告诉我这是必须的。不要告诉我,它为我做的事情比做的更多。我知道没有它怎么生活。我知道如何推出它,因此只有几件事取决于它。如果您要我屈服并让一切接管仅仅是为了与之抗争,那是的,我认为这很糟糕。

我的代码库中没有多余的空间。

至于SOLID和其他设计原则,即使在贫瘠的领域中,我也可以在很大程度上遵循这些原则。不遵循它们会导致另一种问题。


我感谢您的回答,并完全同意某些框架(例如Spring或JPA-Hibernate)已经占领了Java领域。但是,不可否认的是,他们提供了很多Java(JEE)根本没有解决或做错了的良好实践和简化方法。经验,与纯DDD应用程序(带有Rich Domain Models)一起使用的最佳平台/语言/框架是什么?您知道任何好的Github示例吗?如您所说,我想做的很好,而不是坏习惯。
相互依存的

我们不在这里提供资源建议。但是我相信处理框架的适当基础结构层意味着您可以使用任何喜欢的类型。像对待图书馆一样对待他们。将他们的知识放在域之外。另外,不要依赖魔术。抵制使用自己无法创造的事物的冲动。因为有一天可能需要。
candied_orange

回到原始点。SOLID原则如何?富域模型承担许多职责,持久性(JPA),业务逻辑(必须处理事务性)等。另一方面,贫血症模型仅关心具有分离的服务和业务逻辑的持久性。声音也更容易测试。那怎么了
2013年

最主要的是您开始根据系统需求而不是语义需求做出设计选择。它的微妙,易于忽略,成本不明显且难以证明纠正的合理性。简而言之,这就像在争论电力线应该被埋没而不是在民意测验中。一旦人们习惯了廉价的选择,它就不会改变。即使在飓风席卷而来的时候。
candied_orange

1
我想给您带来以下疑问的好处,那就是您的主张:“做得好,丰富的域名会很好;而做得不好,则是一场噩梦。” 也许我从来没有见过它做得很好,但是随后您却胡说八道:“贫血领域总是不好的。” 固执的废话使您的答案一文不值。-1
David Arno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.