【发布时间】:2017-09-11 23:58:50
【问题描述】:
对于命令是否应该有返回值似乎有无穷无尽的困惑。我想知道这种混淆是否仅仅是因为参与者没有说明他们的背景或情况。
混乱
这里是混淆的例子......
-
Udi Dahan 说命令“不会向客户端返回错误”,但in the same article 他展示了一个图表,其中命令确实向客户端返回了错误。
-
Microsoft Press Store 的一篇文章指出“该命令...不返回响应”,但随后又给出了一个模棱两可的警告:
随着围绕 CQRS 的战场经验的增长,一些实践得到巩固并趋于成为最佳实践。与我们刚才所说的部分相反……今天的普遍观点认为命令处理程序和应用程序都需要知道事务操作是如何进行的。必须知道结果...
- Jimmy Bogard 说“commands always have a result”,但随后加倍努力显示命令如何返回 void。
那么,命令处理程序是否返回值?
答案?
从 Jimmy Bogard 的“CQRS Myths”中得到启发,我认为这个问题的答案取决于您所说的程序化/上下文“象限”:
+-------------+-------------------------+-----------------+
| | Real-time, Synchronous | Queued, Async |
+-------------+-------------------------+-----------------+
| Acceptance | Exception/return-value* | <see below> |
| Fulfillment | return-value | n/a |
+-------------+-------------------------+-----------------+
接受(例如验证)
命令“接受”主要是指验证。假设验证结果必须同步地提供给调用者,无论命令“fulfillment”是同步的还是排队的。
但是,似乎许多从业者不会从命令处理程序中启动验证。从我所见,要么是因为(1)他们已经找到了一种在应用程序层处理验证的绝妙方法(即通过数据注释检查有效状态的 ASP.NET MVC 控制器),要么是因为(2)架构已到位,假设命令已提交到(进程外)总线或队列。后一种形式的异步通常不提供同步验证语义或接口。
简而言之,许多设计人员可能希望命令处理程序将验证结果作为(同步)返回值提供,但他们必须接受所使用的异步工具的限制。
履行
关于命令的“执行”,发出命令的客户端可能需要知道新创建记录的 scope_identity 或失败信息 - 例如“帐户透支”。
在实时设置中,返回值似乎最有意义;不应使用异常来传达与业务相关的失败结果。但是,在“排队”上下文中……返回值自然没有意义。
所有的困惑也许可以在这里总结:
许多(大多数?)CQRS 实践者认为他们现在或将来会合并一个异步框架或平台(总线或队列),因此宣称命令处理程序没有返回值。但是,一些从业者无意使用这种事件驱动的构造,因此他们会认可(同步)返回值的命令处理程序。
因此,例如,我相信在Jimmy Bogard provided this sample command interface 时假定了一个同步(请求-响应)上下文:
public interface ICommand<out TResult> { }
public interface ICommandHandler<in TCommand, out TResult>
where TCommand : ICommand<TResult>
{
TResult Handle(TCommand command);
}
他的 Mediatr 产品毕竟是一种内存工具。鉴于这一切,我认为 Jimmy carefully took the time to produce a void return from a command 的原因不是因为“命令处理程序不应该有返回值”,而是因为他只是希望他的 Mediator 类有一个一致的接口:
public interface IMediator
{
TResponse Request<TResponse>(IQuery<TResponse> query);
TResult Send<TResult>(ICommand<TResult> query); //This is the signature in question.
}
...即使不是所有命令都有一个有意义的返回值。
重复和总结
我是否正确地理解了为什么在这个主题上存在混淆?我有什么遗漏吗?
更新(2020 年 6 月)
在给出的答案的帮助下,我想我已经解开了困惑。简而言之,如果 CQRS 命令能够返回指示完成状态的成功/失败,则返回值是有意义的。这包括返回新的数据库行标识,或任何不读取或返回域模型(业务)内容的结果。
我认为出现“CQRS 命令”混淆的地方在于“异步”的定义和作用。 “基于任务的”异步 IO 和异步架构(例如基于队列的中间件)之间存在很大差异。在前者中,异步“任务”可以并且将为异步命令提供完成结果。但是,发送到 RabbitMQ 的命令不会同样收到请求/响应完成通知。正是后一种异步架构的上下文导致一些人说“没有异步命令之类的东西”或“命令不返回值”。
【问题讨论】:
-
很好的问题,但不是很清楚。命令只是 VO,而不是方法,它们不返回任何内容;可能您指的是命令处理;请指定级别/层:表示(即休息)、应用程序、业务级别(聚合)。
标签: design-patterns architecture cqrs command-pattern