当我在上一个项目中使用IoC容器时,最终得到了贫乏的实体以及我在Stateless Services中的大部分业务逻辑。
我见过其他开发人员编写的利用“控制反转”的项目,并且它们始终是“贫血”。
由于“贫血域模型”是反模式,是否可以使用IoC和富域?他们有没有很好的例子,这样做的开源项目?
当我在上一个项目中使用IoC容器时,最终得到了贫乏的实体以及我在Stateless Services中的大部分业务逻辑。
我见过其他开发人员编写的利用“控制反转”的项目,并且它们始终是“贫血”。
由于“贫血域模型”是反模式,是否可以使用IoC和富域?他们有没有很好的例子,这样做的开源项目?
Answers:
首先:DI和IoC 不是同义词。很抱歉,但我必须指出这一点(在我看来,您认为它们是)。
至于您的查询...那么,依赖注入只是一种工具。您将如何使用此工具是完全独立的事情。还有其他工具(设计模式)可能会加重该问题。例如,我认为广泛采用MVC模式是形成Anemic域模型反模式的关键因素之一:控制器(在较简单的应用程序中,在更复杂的应用程序中将是附加的服务层)负责验证业务规则,实施它们以及将数据库实体转换为有用的东西,而业务层则变成简单的数据访问层,该数据访问层是普通的ORM,并且具有与数据库实体的一对一映射。
当然,这就是您设计应用程序的方式—您可以根据需要创建正确的域模型,而所有这些IoC,DI,MVC都不会阻止您。阻止您前进的是您的团队。您需要以某种方式说服他们使用正确的路径,并且由于许多软件开发人员没有强大的架构背景,因此可能很难。
大多数(如果不是全部)应用程序是基础架构和领域关注点的混合体。当您达到一定程度的复杂性时,如果将域与基础架构分开,则将更易于管理,从而使推理和独立发展变得更加容易。
当然,域模型仍然需要与系统的其余部分进行通信,通常这将与无状态服务(属于域的一部分)进行通信,这些服务将注入基础结构问题(例如数据库访问权限)。使用IoC容器并不能消除这种依赖性,而是将其配置移到一个单独的区域中-再次使推理和维护更加容易。
实体正在存储状态,并应负责业务规则。如果您的服务正在强制执行所有不变式和其他业务规则,那么逻辑很可能位于错误的位置。
现在,如果您已将逻辑放在正确的位置,但最终得到的服务不过是围绕基础设施事物和仅仅是属性包的实体的包装,那么该域很可能不够复杂,无法证明其合理性自己模型的开销。您将阅读的有关DDD的几乎所有内容都将包含免责声明,该免责声明实际上仅适用于复杂域,但这似乎常常被人们遗忘。
转到源代码。从Fowler在Anemic Domain Models中的文章开始。他将Eric Evan的域驱动设计作为良好实践的一个例子。源代码在这里。下载它。
观察到它使用控制反转(搜索@Autowired),并具有服务类(BookingService)和“业务流程”类(例如ItineraryUpdater)。
Fowler的原始文章开始了您要寻找的示例的路径。
VoyageRepositoryHibernate
该类放置在基础结构层中,但实际上取决于域层。
save(foo)
的代码在域模型更改时会发生变化(例如,如果将新属性添加到MyDomainObject
),那么它(根据定义)必须属于域层;否则,您就不能再谈论拥有“图层”了。
是否可以使用IoC和Rich Domain?他们有没有很好的例子,这样做的开源项目?
我假设您的意思是DI而不是IoC,并且您从事的项目使用的是像Spring这样的DI容器。IoC有两种主要风格:DI和定位器模式。我不明白为什么定位器模式应该是一个问题,所以让我们关注DI。
我认为这是不可能的,或者至少是非常不切实际的。DI容器的主要方面是,当它们将对象注入其他对象(“托管对象”)时,它们控制对象的创建。项目运行时仍处于活动状态的受管对象集与项目中存在哪些域项目无关,但取决于对象的连接方式以及为其分配的范围(单个对象,原型)。
这就是为什么您不想让DI容器管理您的域对象的原因。但是,如果您手动创建对象(使用新对象),则无法将其他对象注入到域对象中。(将潜在的解决方法与手动布线放在一旁。)由于需要这些注入来将实现替换为其他实现,因此无法使用DI替换富域对象的功能。因此,您将不希望将功能放入域对象中,否则将丢失DI的功能。
我看不到假设的DI容器如何管理不管理您的对象的方式,并且现有的实现都不允许这样做。因此可以断言DI依赖于管理对象。因此,它总是会诱惑您将潜在的Rich Domain对象分成一个贫乏类和一个或几个事务处理脚本类。