类似的问题可能已经被问过很多次了,但我认为每一个回答都有助于更好地理解 DDD。我想描述一下我如何看待 DDD 的某些方面。我对此有一些基本的不确定性,如果有人能提供一个可靠而实用的答案,我将不胜感激。请注意,这些问题假定了 DDD 的“经典”方法。这意味着使用 ORM 等。这里不考虑 CQRS 和事件溯源等方法。
聚合和实体是实现领域逻辑的主要对象。他们有状态和身份。在这种情况下,我将域逻辑视为改变该状态的所有命令的集合。那有意义吗?为什么域逻辑只与状态相关?对没有身份或没有状态的域对象进行建模是否合法?为什么不能将域对象实现为事务脚本?示例:考虑一个向您推荐约会网站合作伙伴的对象。那个对象没有真实的状态,但它做了很多领域逻辑?将其放入服务层意味着领域模型无法涵盖所有逻辑。
访问其他域对象。聚合可以访问存储库吗?示例:当(有状态)域对象需要访问系统的所有“用户”以执行其工作时,它需要通过存储库访问它们。因此,ORM 在加载对象时需要注入存储库(这在技术上可能更具挑战性)。如果对象无法访问存储库,您会将本示例的域逻辑放在哪里?在服务层?服务层不应该没有逻辑吗?
聚合和实体不应该与外界对话,它们只关心它们的有限上下文。我们不应该将外部依赖项(如 IPaymentGateway 或 IEmailService)注入到域对象中,这会导致域处理来自外部的异常。解决方案:基于事件的方法。那你如何发送事件呢?每次实例化域对象时,您仍然需要注入正确的“侦听器”。ORM 是关于恢复“数据”的,但主要不是为了注入依赖项。我们需要 DI-ORM 组合吗?
域对象和 DTO。当您查询聚合根的状态时,它是返回其状态 (DTO) 的投影还是域对象本身?在我看到的大多数模型中,客户端可以完全访问域数据模型,从而引入与域的实际结构的深度耦合。我认为聚合背后的“对象图”是它自己的业务。那是封装,对吧?所以对我来说,聚合根应该只返回 DTO。DTO 通常在服务层中定义,但我的方法是在域本身中对其进行建模。服务层可能仍会添加另一个抽象级别,但这是一个不同的选择。这是个好建议吗?
存储库在聚合根级别处理所有 CRUD 操作。其他查询呢?查询返回 DTO 而不是域对象。为此,存储库必须了解引入耦合的域的数据结构。我的建议与之前类似:使用事件来填充视图。因此,内部结构没有公开,只有事件携带必要的数据来构建视图。
工作单位。系统边界处的控制器将实例化命令并将它们传递给服务层,服务层进而加载适当的聚合并转发命令。控制器可能使用多个命令并将它们传递给多个服务。这都是由工作单元模式控制的。这意味着,存储库、实体、服务——都参与同一个事务。你同意吗?
业务逻辑不是领域逻辑。从业务角度来看,用例的实现可能涉及许多步骤:注册客户、发送电子邮件、创建存储帐户等。整个过程不可能适合域聚合根。域对象需要能够访问所有类型的基础设施。解决方案:工作流或 sagas(或事务脚本)。这是个好建议吗?
谢谢