我正在后端上,尝试实现CQRS模式。 我对事件很清楚,但有时会遇到命令困扰。
我已经看到命令是由用户请求的,例如ChangePasswordCommand
。但是,在实现级别,用户只是调用由某个控制器处理的端点。
我可以向控制器注入UserService
,该控制器将处理域逻辑,这就是基本教程的工作方式(我使用Nest.js)。但是我觉得也许这是我应该使用命令的地方-所以我应该在控制器中执行命令ChangePasswordCommand
,然后域模块会处理它吗?
重要的是我需要从命令返回值,从实现的角度来看这不是问题,但是就CQRS而言,它看起来并不好-我应该同时添加和获取。
或者最后一个选择是在控制器中执行命令,然后在命令处理程序中发出事件(PasswordChangedEvent
)。接下来,等到事件回来并返回控制器中的值。
这最后一个选项对我来说似乎很好,但是我在请求生命周期内实现清晰的实现时遇到了问题。
答案 0 :(得分:2)
尽管@cperson的answer在技术上是正确的,但我想在此添加一些细微之处。
首先,从答案描述中可能不清楚的地方是建议“在命令处理程序中发出事件(PasswordChangedEvent)” 。这也是我更喜欢的,但请注意:
Command
是基础结构层的一部分,而Event
是域的一部分。AggregateRoot
上的代码。mergeObjectContext
或eventBus.publish
(请参见NestJS docs)来完成此操作。我要解决的另一点是假设采用事件源架构,即应用CQRS / ES。尽管CQRS通常与事件来源结合使用,但没有任何规定。事件源可以带来其他优势,但同时也会带来极大的复杂性。您应该仔细权衡使用ES的利弊。
在许多情况下,您不需要事件源。仅拥有CQRS已经为您带来了很多好处,例如拥有完善的域/边界上下文。读写之间的分隔,单一职责命令+查询(通常使用更多SOLID),更简洁的体系结构等。在更高级别上,更容易将重点从转移到我该如何实现这一目标(在CRUD方面)? ',以'这些用户要求如何适应域模型?'。
没有ES,您可以拥有一个关系数据库,例如坚持使用TypeORM。您可以保留事件,但这不是必需的。在许多情况下,您可以避免客户端需要订阅事件的最终一致性(也许您只是使用它们来驱动传奇,并更新读取的视图/投影)。
您总是可以仅从CQRS开始,并在需要时稍后再添加事件源。
答案 1 :(得分:1)
随着体系结构的发展,如果您正在使用Processes / Sagas来管理工作流和聚合之间的通信,则可能会发现您需要命令总线。如果是这样,那么在所有命令中使用该总线自然是有意义的。
以下是我希望使用的方法:
在控制器中执行命令,然后在命令处理程序中发出一个事件(PasswordChangedEvent)。接下来,等到事件回来并返回控制器中的值。
关于实现细节,在.NET中,我们使用SignalR Websockets服务,该服务将读取事件总线(发布所有事件的位置),并将事件转发给已订阅事件的客户端。
在这种情况下,工作流程为: