“控制反转”是否会促进“贫血领域模型”?


32

当我在上一个项目中使用IoC容器时,最终得到了贫乏的实体以及我在Stateless Services中的大部分业务逻辑。

我见过其他开发人员编写的利用“控制反转”的项目,并且它们始终是“贫血”。

由于“贫血域模型”是反模式,是否可以使用IoC和富域?他们有没有很好的例子,这样做的开源项目?


我认为我们需要查看您的特定案例的一些具体示例以提供帮助。
马丁·维尔堡

1
抱歉,我的意思是代码段:)
Martijn Verburg

Answers:


11

首先:DI和IoC 不是同义词。很抱歉,但我必须指出这一点(在我看来,您认为它们是)。

至于您的查询...那么,依赖注入只是一种工具。您将如何使用此工具是完全独立的事情。还有其他工具(设计模式)可能会加重该问题。例如,我认为广泛采用MVC模式是形成Anemic域模型反模式的关键因素之一:控制器(在较简单的应用程序中,在更复杂的应用程序中将是附加的服务层)负责验证业务规则,实施它们以及将数据库实体转换为有用的东西,而业务层则变成简单的数据访问层,该数据访问层是普通的ORM,并且具有与数据库实体的一对一映射。

当然,这就是您设计应用程序的方式—您可以根据需要创建正确的域模型,而所有这些IoC,DI,MVC都不会阻止您。阻止您前进的是您的团队。您需要以某种方式说服他们使用正确的路径,并且由于许多软件开发人员没有强大的架构背景,因此可能很难。


我要补充一点,也许您可​​以看看Eric Evans等人拥护的DDD方法。
马丁·维尔堡

1
我确实读过埃里克·埃文斯(Eric Evans)的书。它对一般方法论和无处不在的语言很有用,但在实际示例中却有些缺乏。
2011年

感谢您指出DI和IoC之间的区别。我认为这个问题与IoC而非DI有关。更改了问题以反映这一点。
2011年

在我与DI框架/容器(春DI,CDI,团结)的经验,他们确实从创建一个“正确的域模型”,这对我来说意味着开发人员不应该从使用真(即,状态)对象限制阻止你。但是DI并不真正支持这一点。
罗杰里奥

8

大多数(如果不是全部)应用程序是基础架构和领域关注点的混合体。当您达到一定程度的复杂性时,如果将域与基础架构分开,则将更易于管理,从而使推理和独立发展变得更加容易。

当然,域模型仍然需要与系统的其余部分进行通信,通常这将与无状态服务(属于域的一部分)进行通信,这些服务将注入基础结构问题(例如数据库访问权限)。使用IoC容器并不能消除这种依赖性,而是将其配置移到一个单独的区域中-再次使推理和维护更加容易。

实体正在存储状态,并应负责业务规则。如果您的服务正在强制执行所有不变式和其他业务规则,那么逻辑很可能位于错误的位置。

现在,如果您已将逻辑放在正确的位置,但最终得到的服务不过是围绕基础设施事物和仅仅是属性包的实体的包装,那么该域很可能不够复杂,无法证明其合理性自己模型的开销。您将阅读的有关DDD的几乎所有内容都将包含免责声明,该免责声明实际上仅适用于复杂域,但这似乎常常被人们遗忘。


7

转到源代码。从Fowler在Anemic Domain Models中的文章开始。他将Eric Evan的域驱动设计作为良好实践的一个例子。源代码在这里。下载它。

观察到它使用控制反转(搜索@Autowired),并具有服务类(BookingService)和“业务流程”类(例如ItineraryUpdater)。

Fowler的原始文章开始了您要寻找的示例的路径。


实际上,该示例应用程序与书中所述的DDD不符。这本书的一个具体矛盾是,它通过包含特定领域的代码而完全违反了“基础设施”的概念。例如,VoyageRepositoryHibernate该类放置在基础结构层中,但实际上取决于域层。
罗杰里奥

是的,这本书在第73页上说,基础结构层在域层“之下”,并且“它不应该对所服务的域有特殊的了解”。这对我来说从来没有任何意义。考虑一个具有两个VoyageRepository实现的项目:VoyageRepositoryHibernate和一个VoyageRepositoryJDBC类。它们的实现方式必须有很大的不同,并且特定于技术。这些属于域层吗?还是基础设施层?根据这本书,在我们的代码中,我们将其反向进行:基础结构层可以引用域层,反之亦然。
杰米

它们属于域层,是的。基于JDBC的实现将包含与特定于域的应用程序数据库中的表和列相关联的SQL代码。在基础架构层中放置任何特定于域或特定于应用程序的代码是错误的,因为“基础结构代码”应仅用于解决技术问题,并且(理想情况下)应在不同的应用程序和域之间完全可重用。在域层中使用“低级”代码(例如SQL)的解决方案不是完全将其移出,而是在更好的基础架构(例如ORM)之上实施。
罗杰里奥

对我来说,save(MyDomainObject foo)的实现纯粹是技术问题。YMMV。
杰米

仅当它不会导致您违反分层体系结构的基本规则时:较低的层不能依赖较高的层。因此,如果您实现save(foo)的代码在域模型更改时会发生变化(例如,如果将新属性添加到MyDomainObject),那么它(根据定义)必须属于域层;否则,您就不能再谈论拥有“图层”了。
罗杰里奥

7

是否可以使用IoC和Rich Domain?他们有没有很好的例子,这样做的开源项目?

我假设您的意思是DI而不是IoC,并且您从事的项目使用的是像Spring这样的DI容器。IoC有两种主要风格:DI和定位器模式。我不明白为什么定位器模式应该是一个问题,所以让我们关注DI。

我认为这是不可能的,或者至少是非常不切实际的。DI容器的主要方面是,当它们将对象注入其他对象(“托管对象”)时,它们控制对象的创建。项目运行时仍处于活动状态的受管对象集与项目中存在哪些域项目无关,但取决于对象的连接方式以及为其分配的范围(单个对象,原型)。

这就是为什么您不想让DI容器管理您的域对象的原因。但是,如果您手动创建对象(使用新对象),则无法将其他对象注入到域对象中。(将潜在的解决方法与手动布线放在一旁。)由于需要这些注入来将实现替换为其他实现,因此无法使用DI替换富域对象的功能。因此,您将不希望将功能放入域对象中,否则将丢失DI的功能。

我看不到假设的DI容器如何管理不管理您的对象的方式,并且现有的实现都不允许这样做。因此可以断言DI依赖于管理对象。因此,它总是会诱惑您将潜在的Rich Domain对象分成一个贫乏类和一个或几个事务处理脚本类。


当谈到富域模型与依赖注入之间的紧张关系时,这个答案确实触手可及。
jrahhali 2015年
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.