CQRS 和命令、CommandHandler 和聚合验证

CQRS and Command, CommandHandler and Aggregate validation

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

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

我想了解将哪个验证放在哪里:

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

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

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

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

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

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

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

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

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

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

因此 - 需要聚合间通信的业务规则验证最好放入 Saga/Process Manager