Martin Fowler认为Anemic Domain Model是一种反模式。
由于Object Relational Impedence Missmatch,将持久性模型作为域模型滚动似乎也很严重。对于持久性和规范化的问题,我们倾向于将类分解为非常小的小块,在这些类之上打字方法是愚蠢的。加上持久性很少发生变化,但业务逻辑变化很大。
所以我们需要一个基于持久性模型构建的DomainModel(而不是同一个)。然后,此域模型将包含业务逻辑属性和方法。
但是现在这些领域模型仍然落后于服务,为了将它们暴露给外界,我们需要将它们转换为DTO。
我们在这里做了manny mappings。
它不会在那里结束,因为DTO可能需要映射到ViewModel。
所有这些以及重复验证逻辑的问题仍然没有消失,因为客户端需要实时验证。 ViewModel对验证一无所知,因此在SPA中,您不得不在客户端(通常使用javascript)再次重写验证逻辑。
此外,服务本质上是无状态的(消息或面向RPC),因此我们在Persistence之间进行所有这些映射,然后返回OO然后返回到Procedural,有什么好处?您如何证明大多数IT预算的实际成本?
我知道如何使用完整的DDD,使用聚合根,域模型等会“很酷”,但是如何证明成本和开发效率的影响呢?
反模式(或反模式)是社交或商业中使用的模式 可能常用的操作或软件工程,但是 在实践中无效和/或适得其反
如果是这样,DDD和Rich Domain Model不会适合上面的反模式定义而不是“精益”域模型。对不起,我鄙视加载的单词“Anemic”。
通过保持域模型,“精益”你实际上允许它被共享而不违反“抽象依赖原则”,“不要重复自己”以及映射一个数据载体的耗时,繁琐且容易出错的过程到另一个,以及除此之外的任何相关单元测试(除非您考虑使用单元测试进行映射并希望最好)。
答案 0 :(得分:7)
似乎你正在混淆很多概念,指责丰富的领域模型方法,而不是直接负责的事情。
富域模型与分层体系结构正交,特别是富域模型并不表示您拥有的层数,应在这些层之间交换哪些数据结构以及应如何映射它们。
丰富的域模型与验证正交,除了后端验证之外,没有说明客户端检查的必要性。
换句话说,使用服务中的所有业务逻辑使您的域模型贫血,不一定会使您免于编写大量样板DTO映射代码,也不会消除客户端“双重检查”的需要(其中顺便说一下,这是一种普遍接受的最佳做法。)
这并不意味着您对完整多层架构的成本和重量的观点无效。您可能会对Mark Seemann讨论类似问题的帖子感兴趣:http://blog.ploeh.dk/2012/02/09/IsLayeringWorthTheMapping.aspx
答案 1 :(得分:5)
tl; dr域模型定义不明确,它可能是以数据库为中心的方法设计的。
DDD的主要目的是在代码中建模业务概念和流程。我真的怀疑你的商业概念和流程只是一揽子财产。但如果它们确实是肯定的,那么域模型可以与持久性模型相同,因此您不必进行任何映射。
持久性模型模拟对象状态的存储方式。如果您不在数据库域中,则域和持久性模型不能具有相同的目的。我在DDD中看到的最大错误之一是考虑领域模型,就像它仍然是数据驱动的。在DDD中,您没有具体的数据库。你有存储库。没有一对多,多对多等关系。没有表,也没有行。只有尝试尽可能准确地表示域的对象。
简单地说,Domain关心建模业务行为,而Persistence关心存储对象状态。我在这里看到两个不同的责任。
关于验证,您有两种类型:验证输入数据格式,然后根据您在对象状态中更改的内容验证其他业务规则。
关于复制,我认为你指的是输入格式,但像@EbenRoux所说有机制可以帮助解决这个问题。在asp.net mvc中,大多数验证规则也包含js版本。
让我告诉你服务的一个小秘密。虽然可以在域中定义其接口,但它们的实现可以位于持久层中,从而允许它们直接与存储一起工作。由于应用程序的其余部分以抽象形式(界面)使用服务,因此只有DI容器才会知道脏密码。
DDD不是酷,而是根据域设计应用程序。我敢打赌很少开发一个应用程序,唯一的目的是成为数据库的UI。大多数人的目的是通过他们的软件提供服务,以构建解决问题的虚拟产品。 有意义的是,应用程序的设计可以解决您想要解决的问题,而不是您刚刚使用的技术工具。
这听起来怎么样,你想要一个砖房,但是建造者说:“对不起,但我看到的只适用于木头”。好吧,不要使用锯,使用其他工具可以帮助切砖。这些工具需要针对这个问题进行调整,而不是反过来。
答案 2 :(得分:3)
首先,我不认为您可以轻易地从服务器上的客户端和上复制验证逻辑。但是,这不仅限于DDD。有一些机制可以缓解疼痛,但总是需要付出一些努力。
另一部分是整个地图业务:)
您正在做的是有效地使用执行读取。您可能认为需要阅读实体才能进行编辑。如果您在实体对象上执行基于实体(实体可能更多地在数据库术语中 - 整个记录)操作而不是基于任务的操作,那么情况就是如此。一个愚蠢的例子可能是客户打电话给呼叫中心更改地址。操作员调出客户记录并编辑地址。这是基于实体的,并导致典型问题w.r.t.并发,因为可以对同一记录执行2个动作(但不太可能)。这是一种非常传统的用户体验设计方法:"编辑记录"。
将此与屏幕上的按钮进行对比,该按钮显示:" 更改地址"。你只更改了记录上的地址,虽然这看起来像是一样的,但它确实是完全不同的。改变相同地址的2次操作的机会比改变相同记录的机会要小得多。如果需要,可以在此部分进行并发检查。
现在,如果一个人没有读取域,那么人们会读什么。数据来自何处。这就是CQRS(命令/查询责任隔离)的用武之地。过去,它与事件采购混淆/结合,但这不是必需的。您可以为应用程序创建一个简单的查询端,专注于返回所需的数据。在C#中,如果它是单个实例,则使用DataRow
,对于多个实例使用DataTable
,对于任何更复杂的实例使用自定义DTO。可能有一种方法甚至可以逃脱匿名类型(尚未对此进行调查)。
因此:
域模型=操作/计算/写入 查询服务=读取
在某种情况下,您可以轻松加载实体/聚合,例如在Web应用程序中,因为它知道(或可能知道)您的域模型,但智能客户端会有点反图案。
理由相当棘手,但它归结为维护。如果您的方法不能减轻您的维护负担,那么可能无法正确应用某些内容并需要进行重构。
DDD不仅仅涉及技术实施,尽管这有很长的路要走,正朝着正确的OO建模方向发展。我想无论如何其他想法都会加入到软件中,所以无论如何软件似乎都成了焦点。我们都希望看到橡胶与道路相遇的地方。
像大多数事情一样,DDD可能做错了:)