30

我最近阅读了CQRS à la Greg Young的文章,我仍在努力了解 CQRS。

我不确定应该在哪里进行输入验证,以及是否可能必须在两个不同的位置进行(因此违反了不要重复自己的规则,也可能违反了关注点分离)。

给定以下应用程序架构:

#      +--------------------+           ||
#      |    event store     |           ||
#      +--------------------+           ||
#           ^          |                ||
#           |  events  |                ||
#           |          v                  
#      +--------------------+         events         +--------------------+
#      |      domain/       | ---------------------> |   (denormalized)   |
#      |  business objects  |                        |  query repository  |
#      +--------------------+           ||           +--------------------+
#         ^   ^   ^   ^   ^             ||                      |
#         |   |   |   |   |             ||                      |
#      +--------------------+           ||                      |
#      |    command bus     |           ||                      |
#      +--------------------+           ||                      |
#                ^                                              |
#                |             +------------------+             |
#                +------------ |  user interface  | <-----------+
#                  commands    +------------------+        UI form data  
  • 该域隐藏在命令总线后面的 UI 中。也就是说,UI 只能向域发送命令,而不能直接访问域对象。

  • 当聚合根对事件做出反应时,不得进行验证,但更早。

  • 命令被转换为域中的事件(通过聚合根)。这是可能发生验证的地方:如果无法执行命令,则不会将其转换为相应的事件;相反,(例如)抛出一个异常,该异常通过命令总线向上冒泡,返回 UI,在那里它被捕获。

问题:

如果命令无法执行,我想禁用 UI 中的相应按钮或菜单项。但是我怎么知道一个命令在发送之前是否可以执行呢?查询端在这里对我没有帮助,因为它不包含任何业务逻辑;我在命令方面所能做的就是发送命令。

可能的解决方案:

  • 对于任何命令DoX,引入一个相应的虚拟命令CanDoX,它实际上不会做任何事情,但让域反馈命令X是否可以无错误地执行。

  • 在 UI 中复制一些验证逻辑(真正属于域)。

显然第二种解决方案是不利的(由于缺乏关注点分离)。但是第一个真的更好吗?

4

2 回答 2

9

我想我的问题刚刚被另一篇文章解决了, Udi Dahan 的Clarified CQRS。“命令和验证”部分开头如下:

命令和验证

在考虑可能导致命令失败的原因时,出现的一个主题是验证。验证与业务规则的不同之处在于它声明了一个与命令相关的上下文无关的事实。命令要么有效,要么无效。另一方面,业务规则依赖于上下文。

[…] 即使一个命令可能是有效的,仍然可能有拒绝它的理由。

因此,可以在客户端上执行验证,检查该命令所需的所有字段是否存在,数字和日期范围是否正常,诸如此类。服务器仍将验证所有到达的命令,而不信任客户端进行验证。

我认为这意味着——考虑到我有一个基于任务的 UI,正如通常建议 CQRS 正常工作(命令作为域动词)——我只会在命令不能时灰显(禁用)按钮或菜单项由于命令所需的某些数据仍然丢失或无效而被发送;IE。UI 对命令本身的有效性作出反应,而不是对命令对域对象的未来影响作出反应。

因此,不需要CanDoX命令,也不需要将域验证逻辑泄漏到 UI 中。然而,UI 将拥有一些用于命令验证的逻辑。

于 2011-03-06T21:04:04.393 回答
2

客户端验证基本上仅限于格式验证,因为客户端无法知道服务器上数据模型的状态。现在有效的,可能在 1/2 秒后无效。

因此,客户端应仅检查是否填写了所有必填字段以及它们的格式是否正确(电子邮件地址字段必须包含有效的电子邮件地址,例如格式 (.+)@(.+)。 (.+) 等)。

然后,所有这些验证以及业务规则验证都在 Command 服务中的域模型上执行。因此,在客户端验证的数据仍然可能导致服务器上的命令无效。在这种情况下,一些反馈应该能够返回到客户端应用程序......但这是另一回事。

于 2011-03-10T12:42:26.750 回答