可能已经多次提出类似的问题,但我认为每一个回答都有助于更好地理解DDD。我想描述一下我如何看待DDD的某些方面。我有一些基本的不确定性,如果有人可以提供一个坚实和实用的anwser,我将不胜感激。请注意,这些问题假设采用DDD的“经典”方法。这意味着使用ORM等。此处不考虑CQRS和事件采购等方法。
聚合和实体是实现域逻辑的主要对象。他们有国家和身份。在这种情况下,我将域逻辑视为改变该状态的所有命令的集合。那有意义吗?为什么域逻辑只与状态相关?对没有身份或没有状态的域对象进行建模是否合法?为什么域对象不能作为事务脚本实现?示例:考虑一个建议您成为约会网站合作伙伴的对象。那个对象没有真正的状态,但它确实有很多域逻辑?将其放入服务层意味着域模型不能涵盖所有逻辑。
访问其他域对象。聚合可以访问存储库吗?示例:当(有状态)域对象需要访问系统的所有“用户”以执行其工作时,它需要通过存储库访问它们。因此,ORM在加载对象时需要注入存储库(这在技术上可能更具挑战性)。如果对象无法访问存储库,那么您将在何处放置此示例的域逻辑?在服务层?服务层不应该没有逻辑吗?
聚合和实体不应该与外界交谈,他们只关心他们有限的背景。我们不应该将外部依赖项(如IPaymentGateway或IEmailService)注入域对象,这会导致域处理来自外部的异常。解决方案:基于事件的方法。你怎么发送事件呢?每次实例化域对象时,仍需要注入正确的“侦听器”。 ORM是关于恢复“数据”,但主要不是为了注入依赖关系。我们需要DI-ORM混合物吗?
域对象和DTO。当您查询聚合根的状态时,它是否返回其状态(DTO)或域对象本身的投影?在我看到的大多数模型中,客户端可以完全访问域数据模型,从而引入与域的实际结构的深度耦合。我认为聚合背后的“对象图”是它自己的商业。这是封装,对吗?所以对我来说,聚合根应该只返回DTO。 DTO通常在服务层中定义,但我的方法是在域本身中对其进行建模。服务层可能仍会添加另一个抽象级别,但这是一个不同的选择。这是一个很好的建议吗?
存储库处理聚合根级别的所有CRUD操作。其他查询怎么样?查询返回DTO而不是域对象。为了实现这一点,代表必须了解引入耦合的域的数据结构。我的建议与之前类似:使用事件来填充视图。因此,内部结构不公开,只有事件带有必要的数据来构建视图。
工作单位。系统边界处的控制器将实例化命令并将它们传递给服务层,服务层又加载适当的聚合并转发命令。控制器可能使用多个命令并将它们传递给多个服务。这完全由工作单元模式控制。这意味着,存储库,实体,服务 - 都参与同一个事务。你同意吗?
商业逻辑不是域逻辑。从商务角度来看,用例的实现可能涉及许多步骤:注册客户,发送电子邮件,创建存储帐户等。这整个过程可能不可能适合域聚合根。域对象需要访问所有类型的基础结构。解决方案:工作流程或传奇(或事务脚本)。这是一个很好的建议吗?
谢谢
答案 0 :(得分:6)
我建议的第一个最佳做法是read the Evans' book。的两次即可。
太多的“DDD项目”失败了,因为开发人员假装DDD只是简单地完成了OOP。
然后,您应该真正了解DDD适用于必须处理非常复杂的业务规则正确的应用程序。简而言之:如果您不需要向域专家付费以了解业务,则不需要DDD。事实上,DDD的核心概念是编码人员,专家和用户共同理解的ubiquitous language。
此外,您应该通过阅读Effective Aggregate Design by Vernon来阅读并理解聚合是什么(一致性边界)。
最后,您可能会发现有用的the modeling patterns documented here。
答案 1 :(得分:5)
尽管我上面的评论,但我还是抨击了你的观点。 (注意:我不是埃里克埃文斯或吉米尼尔森,所以请用我的“建议”加盐)。
您的示例“考虑一个推荐您成为约会网站合作伙伴的对象。”,属于域服务(不是基础结构服务)。在此处查看此文章 - http://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/
聚合不直接访问存储库,但它们可以创建一个工作单元,将多个域对象的操作合并为一个。
对此不确定。这应该是一个问题。
理论上,这是有争议的,域实体不会在聚合根之外直接可用,但这并不总是实用的。我会根据具体情况考虑这个决定。
我不确定“查询”究竟是什么意思。如果对您域中所有可能的“阅读”方案进行建模似乎不切实际或提供足够的性能,则表明CQRS解决方案可能是最佳解决方案。
是的,我同意。 UOW是工具箱中的一个工具,可以在各个层中使用。
这句话从根本上说是错误的“业务逻辑不是域逻辑”。域是表示业务逻辑,因此使用ubiquitous language
。