在CQRS中,他们的命令和查询是否属于域?
事件是否也属于域?
如果是这种情况,那么命令/查询处理程序只是在基础架构中实现吗?
现在我把它放在这样的位置:
Application.Common
Application.Domain
- Model
- Aggregate
- Commands
- Queries
Application.Infrastructure
- Command/Query Handlers
- ...
Application.WebApi
- Controllers that utilize Commands and Queries
另一个问题,你从哪里举起活动?命令处理程序或域聚合?
答案 0 :(得分:41)
命令和事件可能是非常不同的问题。它们可能是技术问题,集成问题,域名问题......
我认为,如果您询问域名,您就会实施域名模型(甚至可能使用域名驱动设计)。
如果是这种情况,我会尽量给你一个非常简化的回复,这样你就可以有一个起点:
如果是这种情况,那么命令/查询处理程序只是在基础架构中实现吗?
命令处理程序在语义上类似于应用程序服务层。通常,应用程序服务层负责编排域。它通常围绕业务用例构建,例如"下订单"。在这些用例中,通过聚合根,查询等来调用业务逻辑(应始终封装在域中)。它也是处理cross cutting concerns的好处,如事务,验证,安全性等。
但是,应用程序层不是必需的。它取决于功能和技术要求以及已经制定的架构选择。 你的发言似乎是正确的。我最好将命令处理程序保留在系统的边界。如果没有合适的应用程序层,命令处理程序可以扮演用例orchestrator的角色。如果将其放在域中,您将无法轻松处理横切问题。这是一种权衡。您应该了解解决方案的利弊。它可以在一个案例中起作用而在另一个案例中起作用。
至于事件处理程序。我一般在
处理它无论如何,你不应盲目遵守规则。总会有权衡,可以找到不同的方法。
另一个问题,你从哪里举起活动?命令处理程序或域聚合?
我是从域聚合根目录进行的。因为域负责引发事件。
由于始终存在技术规则,如果在聚合中持续存在更改的问题,则不应发布事件,反之亦然,我采用事件采购中使用的方法,这是务实的。我的聚合根有一组Unpublished
个事件。在我的存储库的实现中,我将检查Unpublished
事件的集合并将它们传递给负责发布事件的中间件。很容易控制,如果持久存在聚合根,则不会发布事件。有人说它不是存储库的责任,我同意,但是谁在乎。选择是什么。使用所有基础架构问题(事务,异常处理等)进入事件发布的尴尬代码,或者实用并处理Infrastructure层中的所有内容?我已经完成了两个并且相信我,我更愿意务实。
总而言之,没有一种做事方式。始终了解您的业务需求和技术要求(可扩展性,性能等)。而不是基于此做出选择。我已经描述了我在大多数情况下所做的一般工作。这只是我的意见。
答案 1 :(得分:9)
在某些implementations中,命令和处理程序位于Application层中。 In others,他们属于域名。我经常在OO系统中看到前者,后者在功能实现中更多,这也是我自己做的,但是YMMV。
如果事件是指域事件,那么......是的,我建议在域层中定义它们并从域对象中发出它们。领域事件是您无处不在的语言的重要组成部分,如果您练习Event Storming,甚至可以由领域专家直接创造,因此将它们放在那里绝对是有意义的。
我认为你应该记住的是,对于大多数这些技术细节都没有规定,这些细节值得一成不变。关于DDD模板项目以及SO上的分层和代码“地形”存在无数问题,但坦率地说,我认为这些问题在制作强大,高性能和可维护的应用程序时不具有决定性作用,特别是因为它们与上下文有关。您很可能不会为每分钟有数百万次汇总更改的交易系统组织代码,就像50人使用博客发布平台一样,即使两者都采用DDD方法设计。有时你必须根据你的背景为自己尝试一些事情,并一路学习。
答案 2 :(得分:2)
命令和事件是DTO。您可以在任何层/组件中拥有命令处理程序和查询。事件只是通知某些内容发生了变化。您可以拥有所有类型的事件:域,应用程序等。
事件可以由处理程序生成并汇总到您自己。但是,无论它们在何处生成,命令处理程序都应使用服务总线来发布事件。我更喜欢在聚合根目录中生成域事件。
从DDD战略角度来看,只有业务概念和用例。域事件,命令,处理程序是技术细节。但是,所有域用例通常都是作为命令处理程序实现的,因此命令处理程序应该是域的一部分,以及实现域使用的查询的查询处理程序。 UI使用的查询可以是UI的一部分,依此类推。
CQRS的要点是至少有2个模型,命令应该是域模型本身。但是,您可以使用查询模型,专门用于域使用,但它仍然是读取(简化)模型。将命令模型视为仅用于更新,读取模型仅用于查询。但是,您可以拥有多个读取模型(由特定层或组件使用)或只有一个通用(用于所有查询)。