C# CQRS和命令、CommandHandler和聚合验证

C# CQRS和命令、CommandHandler和聚合验证,c#,asp.net-core,cqrs,C#,Asp.net Core,Cqrs,我正在一个新项目中实施CQR的第一步。 遵循CQRS方法,我使用MediatR作为框架来编排我的命令 我还使用FluentValidation和ValidationBehavior来实现命令验证。但我正在努力定义将哪些验证放在何处: 在命令中(使用带有FluentValidation的CommandValidator) 在命令处理程序中,作为自定义检查 在聚合根目录中(直接在此聚合上调用操作时) 我想了解将哪个验证放在哪里: 字段验证(长度、格式、必填项等) 需要实现IRepository

我正在一个新项目中实施CQR的第一步。 遵循CQRS方法,我使用MediatR作为框架来编排我的命令

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

  • 在命令中(使用带有FluentValidation的CommandValidator)
  • 在命令处理程序中,作为自定义检查
  • 在聚合根目录中(直接在此聚合上调用操作时)
我想了解将哪个验证放在哪里:

  • 字段验证(长度、格式、必填项等)
  • 需要实现IRepository的业务规则验证(例如,基于列而非主键的唯一性)
  • 不需要实现IRepository的业务规则验证
由于我目前已经实现了它,因此我无法访问我的聚合根目录中的IRepository实现。DI注入此存储库的是命令处理程序,它们在聚合根上执行操作,但也负责调用,例如保存或添加,以最终更新数据库。

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

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

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

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

在某些情况下,聚合无法在不查询其他聚合状态的情况下完全检查命令的正确性。这是你的电话号码。简言之,您不应该在命令执行期间与其他聚合交谈

典型的例子是创建具有唯一名称的用户。社会共识似乎是:

  • 在发送命令之前,验证用户名在客户端中是否唯一

  • 用户聚合在不检查唯一性的情况下执行命令

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


因此-业务规则验证需要更好地进行聚合间通信,以便将其放入Saga/Process Manager中

您在分离3种类型的规则方面走的正是正确的道路。用于字段验证的Command vs Command handler是基于意见的,但是使用存储库的问题与DDD的更基本概念有关,这些概念已经在本文中详细介绍过。您可能会在搜索“交叉聚合不变量”时找到答案。您将在“设置验证”下找到唯一性的具体问题。如果您验证命令中用户名的唯一性,这是否意味着命令/控制器需要依赖于您的特定存储库来检查是否存在?是的,它会。但我的意思是,您不应该在命令处理程序中执行任何查询。主要原因是这些查询的结果仍然不一致。