贫血领域模型

2003年11月25日

这是一种存在了很长时间的反模式,但最近似乎特别流行。我和埃里克·埃文斯聊过这个话题,我们都注意到它们似乎越来越受欢迎。作为对适当领域模型的强烈支持者,这不是一件好事。

贫血领域模型的基本症状是,乍一看它看起来像是真东西。有对象,许多对象以领域空间中的名词命名,这些对象通过真实领域模型所具有的丰富关系和结构连接在一起。当您查看行为时,就会发现这些对象几乎没有行为,使它们仅仅是 getter 和 setter 的集合。事实上,这些模型通常带有设计规则,这些规则规定您不应在领域对象中放置任何领域逻辑。相反,有一组服务对象捕获所有领域逻辑,执行所有计算并使用结果更新模型对象。这些服务位于领域模型之上,并使用领域模型进行数据操作。

这种反模式的根本恐怖之处在于它与面向对象设计的基本理念背道而驰;即结合数据和过程。贫血领域模型实际上只是一种过程式设计风格,正是像我(和埃里克)这样的面向对象狂热分子从 Smalltalk 的早期就开始反对的那种东西。更糟糕的是,许多人认为贫血对象是真正的对象,因此完全错过了面向对象设计本质的意义。

现在,面向对象的纯粹主义很好,但我意识到我需要更多反对这种贫血的根本论据。本质上,贫血领域模型的问题在于它们承担了领域模型的所有成本,却没有带来任何好处。主要成本是映射到数据库的笨拙性,这通常会导致一层 O/R 映射。只有当您使用强大的 OO 技术来组织复杂逻辑时,这才是值得的。但是,通过将所有行为都提取到服务中,您实际上最终得到了事务脚本,从而失去了领域模型可以带来的优势。正如我在P of EAA中所讨论的,领域模型并不总是最好的工具。

同样值得强调的是,将行为放入领域对象不应与使用分层来将领域逻辑与持久性和表示责任等内容分离的稳固方法相矛盾。应该放在领域对象中的逻辑是领域逻辑 - 验证、计算、业务规则 - 无论您想称之为什么是都可以。(在某些情况下,您可以为将数据源或表示逻辑放入领域对象提出论据,但这与我对贫血的看法无关。)

所有这些混乱的一个来源是,许多 OO 专家确实建议在领域模型之上放置一层过程式服务,以形成服务层。但这并不是要使领域模型没有行为的理由,事实上,服务层倡导者在行为丰富的领域模型的基础上使用服务层。

埃里克·埃文斯的优秀著作领域驱动设计对这些层有以下说法。

应用程序层 [他称之为服务层]:定义软件应该执行的任务,并指导表达性的领域对象解决问题。此层负责的任务对业务有意义,或者对与其他系统的应用程序层进行交互是必要的。此层保持精简。它不包含业务规则或知识,只协调任务并将工作委托给下一层中领域对象的协作。它没有反映业务状况的状态,但它可以具有反映用户或程序任务进度的状态。

领域层(或模型层):负责表示业务概念、有关业务状况的信息以及业务规则。反映业务状况的状态在此处进行控制和使用,即使存储它的技术细节被委托给基础设施。此层是业务软件的核心。

这里的关键点是服务层很薄 - 所有关键逻辑都位于领域层。他在他的服务模式中重申了这一点。

现在,更常见的错误是过早地放弃将行为融入适当的对象,逐渐滑向过程式编程。

我不知道为什么这种反模式如此普遍。我怀疑这是由于许多人没有真正使用过适当的领域模型,特别是如果他们来自数据背景的话。一些技术鼓励这样做;例如 J2EE 的实体 Bean,这也是我更喜欢POJO 领域模型的原因之一。

一般来说,您在服务中发现的行为越多,您就越有可能剥夺自己使用领域模型的益处。如果所有逻辑都在服务中,那么您就彻底剥夺了自己。