26

Martin Fowler 认为贫血域模型是一种反模式。

由于Object Relational Impedence Missmatch ,将持久性模型作为域模型滚动似乎也严重偏离了。为了持久性和规范化,我们倾向于将类分解为非常小的部分,在这些类之上使用方法是愚蠢的。加上持久性很少改变,但业务逻辑改变了一点。

所以我们需要一个建立在持久性模型上的 DomainModel(而不是一模一样)。然后,此领域模型将包含业务逻辑属性和方法。

但是现在这些领域模型仍然在服务背后,为了将它们暴露给外部世界,我们需要将它们转换为 DTO。

我们在这里做了很多映射。

  1. 领域模型的持久性
  2. 将域模型转换为 DTO 以在服务之间传递

它并没有就此结束,因为可能需要将 DTO 映射到 ViewModel 中。

所有这些以及重复验证逻辑的问题仍然没有消失,因为客户端需要实时验证。ViewModel 对验证一无所知,因此例如在 SPA 中,您不得不在客户端(通常在 javascript 中)再次重写验证逻辑。

此外,服务本质上是无状态的(面向消息或 RPC),所以我们正在做所有这些映射,在 Persistence 之间,到 OO,然后再回到 Procedural,有什么好处?在大多数 IT 预算中,您如何证明成本的实际价值?

我知道拥有完整的 DDD、聚合根、域模型等是多么“酷”,但你如何证明成本和对开发生产力的影响是合理的?

反模式(或反模式)是一种在社会或商业运营或软件工程中使用的模式,可能常用但在实践中无效和/或适得其反

如果是这样,那么 DDD 和富域模型是否比“精益”域模型更适合上面的反模式定义。对不起,我鄙视加载的词,“贫血”。

通过保持领域模型“精益”,您实际上允许共享它,而不会违反“抽象依赖原则”、“不要重复自己”以及将一个数据载体映射到另一个数据载体的耗时、乏味且容易出错的过程,以及任何相关的单元测试(除非您正在考虑进行不带单元测试的映射并希望获得最好的结果)。

4

3 回答 3

9

tl;dr 域模型定义不明确,它可能在设计时考虑了以数据库为中心的方法。

DDD 的主要目的是在代码中对业务概念和流程进行建模。我真的怀疑您的业务概念和流程只是一堆属性。但如果它们真的是,那么领域模型可以与持久性模型相同,因此您不必进行任何映射。

持久性模型对对象状态的存储方式进行建模。如果您不在数据库领域,那么领域和持久性模型就不能有相同的目的。我在 DDD 中看到的最大错误之一是认为域模型仍然是数据驱动的。在 DDD 中,您没有具体的数据库。你有存储库。没有一对多、多对多等关系。没有表格,也没有行。只有对象试图尽可能准确地表示域。

简而言之,Domain 关心对业务行为建模,而 Persistence 关心存储对象状态。我在这里看到了两种不同的责任。

关于验证,您有两种类型:输入数据格式的验证,然后根据您在对象状态中更改的内容验证其他业务规则。

关于重复,我认为您指的是输入格式,但就像@EbenRoux 所说的那样,有一些机制可以帮助解决这个问题。在 asp.net mvc 中,大多数验证规则也包含 js 版本。

让我告诉你一个关于服务的小秘密。虽然它们的接口可以在域中定义,但它们的实现可以位于持久层中,从而允许它们直接使用存储。而且由于应用程序的其余部分以抽象形式(接口)使用服务,因此只有 DI 容器会知道肮脏的秘密。

DDD 不是要酷,而是要根据领域设计应用程序。我敢打赌,很少有人会开发一个应用程序,其唯一目的是成为数据库的 UI。大多数人旨在通过他们的软件提供服务,构建解决问题的虚拟产品。设计应用程序是为了解决您想要解决的问题,而不是您碰巧使用的技术工具,这是有道理的。

这听起来如何,你想要一栋砖房,但构造函数说:“对不起,我的锯子只适用于木头”。好吧,不要使用锯子,使用其他可以帮助切割砖块的工具。这些工具需要针对问题进行调整,反之亦然。

于 2013-01-17T08:38:47.110 回答
9

您似乎混淆了很多概念,将富域模型方法归咎于它不直接负责的事情。

  • 富域模型与分层架构正交,特别是拥有富域模型并不决定您拥有的层数、这些层之间应该交换哪些数据结构以及它们应该如何映射。

  • 富域模型与验证是正交的,除了后端验证之外,没有说明需要客户端检查。

换句话说,使您的域模型与服务中的所有业务逻辑无关,不一定会让您免于编写大量样板 DTO 映射代码,也不会消除对客户端“双重检查”的需要(这是由方式普遍接受的最佳实践)。

这并不意味着您关于成熟的多层架构的成本和重量的观点是无效的。您可能会对 Mark Seemann 讨论类似问题的帖子感兴趣:http: //blog.ploeh.dk/2012/02/09/IsLayeringWorthTheMapping.aspx

于 2013-01-17T16:42:02.340 回答
5

首先,我不认为您可以真正轻松地摆脱在客户端服务器上重复验证逻辑。但是,这不仅限于 DDD。有一些机制可以缓解疼痛,但总是需要一些努力。

另一部分是整个地图业务:)

您正在做的是有效地用于执行读取。您可能认为您需要阅读您的实体才能对其进行编辑。如果您在实体对象上执行基于实体(此处可能更多地用数据库术语表示实体 - 整个记录)的操作,而不是基于任务的操作,这是正确的。一个愚蠢的例子可能是客户打电话到呼叫中心更改地址。接线员调用客户记录并编辑地址。这是基于实体的,并且会导致典型的并发问题,因为可能会在同一记录上执行 2 个操作(但不太可能)。这是一种非常传统的 UX 设计方法:“编辑记录”。

将此与屏幕上的一个按钮进行对比,该按钮显示:“更改地址”。您只需更改记录上的地址,虽然这看起来是一回事,但实际上却大不相同。两次更改相同地址的操作的机会比更改相同记录的机会要小得多。如果需要,可以在这部分进行并发检查。

现在,如果一个人不阅读该领域会读什么。数据从何而来。这就是 CQRS(命令/查询职责分离)的用武之地。过去它与事件溯源混淆/结合,但这不是必需的。您可以为您的应用程序创建一个简单的查询端,专注于返回您需要的数据。在 C# 中,我使用 a DataRowif 它是单个实例,aDataTable用于多个实例,以及自定义 DTO 用于更复杂的任何事情。可能有一种方法甚至可以摆脱匿名类型(尚未对此进行调查)。

所以:

领域模型 = 操作 / 计算 / 写入 查询服务 = 读取

在某些情况下,您可以简单地加载实体/聚合,例如在 Web 应用程序中,因为它知道(或可能知道)您的域模型,但智能客户端会有点反模式。

理由相当棘手,但归结为维护。如果您的方法没有减轻您的维护负担,那么很可能某些东西没有正确应用并且需要一些重构。

DDD 不仅仅是关于技术实现,尽管它还有很长的路要走,朝着正确的 OO 建模方向发展。我猜其他想法无论如何都会输入到软件中,所以无论如何软件似乎都是焦点。我们都想看看橡胶与道路的交汇点。

像大多数事情一样,DDD 可能会做错:)

于 2013-01-17T04:58:32.760 回答