【问题标题】:CQRS and Command, CommandHandler and Aggregate validationCQRS 和 Command、CommandHandler 和聚合验证
【发布时间】:2019-03-24 02:02:33
【问题描述】:

我正在为一个新项目实施 CQRS 迈出第一步。 按照 CQRS 方法,我使用 MediatR 作为框架来编排我的命令。

我还使用 FluentValidation 和 ValidationBehavior 来实现命令验证。但我正在努力定义在哪里放置验证:

  • 在命令中(使用带有 FluentValidation 的 CommandValidator)
  • 在命令处理程序中,作为自定义检查
  • 在聚合根中(直接在此聚合上调用操作时)

我想了解在哪里放置验证:

  • 字段验证(长度、格式、必需...)
  • 需要实现 IRepository 的业务规则验证(例如,基于列而不是主键的唯一性)
  • 不需要实现 IRepository 的业务规则验证

因为我目前已经实现了它,所以我无法访问我的 聚合 根目录中的 IRepository 实现。由 DI 注入这个存储库的是命令处理程序,它们在聚合根上执行操作,但也负责调用例如保存或添加以最终更新数据库。

【问题讨论】:

  • 您在区分这 3 种规则方面是完全正确的。用于字段验证的命令与命令处理程序是基于意见的,但使用存储库的问题与已在 SO 中详细介绍的 DDD 的更基本概念有关。您可能会找到搜索“交叉聚合不变量”的答案。您可以在“设置验证”下找到关于唯一性的具体问题。

标签: c# asp.net-core cqrs


【解决方案1】:

在“理想”CQRS 中,您的聚合状态应该包含执行命令所需的所有内容,并且在命令执行期间您不应该与其他聚合通信(因为它们超出了一致性边界)

因此业务规则验证(决定是否可以执行给定命令)应该转到聚合的命令处理程序。

在发送命令之前应检查命令格式规则(必填字段、允许的值) - 在客户端上,以及在将命令传递给命令处理程序之前(因为我们不能完全信任客户端)。

因此,我会将命令格式验证规则保留在更接近命令定义的位置 - 可能采用声明形式,而不是作为专用验证器。

在某些情况下,如果不查询其他聚合的状态,聚合就无法完全检查命令的正确性。这是good article about inter-aggregate communication。简而言之 - 您不应该在命令执行期间与其他聚合体交谈。

经典示例是创建具有唯一名称的用户。社区的共识似乎是:

  • 您验证用户名在客户端中是唯一的 - 在发送命令之前。

  • 用户聚合执行命令而不检查唯一性。

  • 在您发送命令之前有人注册相同用户名的极少数情况会在投影代码中捕获,并导致手动解决的异常。或者有一些传奇会监视重复的用户名并以某种方式处理这个问题。

所以 - 需要将聚合间通信更好地放入 Saga/Process Manager 的业务规则验证

【讨论】:

  • 如果您在命令中验证用户名的唯一性,这是否意味着命令/控制器需要依赖于您的特定存储库才能检查存在?
  • 是的,会的。但我是说你不应该在命令处理程序中做任何查询。主要原因是这些查询的结果仍然不一致。
猜你喜欢
  • 2012-09-06
  • 2014-08-19
  • 1970-01-01
  • 2015-10-06
  • 1970-01-01
  • 2020-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多