【问题标题】:Should I ignore the guidance and avoid putting validation in the command objects?我应该忽略指导并避免在命令对象中进行验证吗?
【发布时间】:2018-11-18 11:15:55
【问题描述】:

我正在使用 CQRS。我读到的所有地方都告诉我将验证逻辑放在命令对象中。例如,看这个链接:https://lostechies.com/jimmybogard/2016/04/29/validation-inside-or-outside-entities/

请看下面的命令(取自链接):

public class ChangeNameCommand { 
   [Required] 
   public string FirstName { get; set; } 
   [Required] 
   public string LastName { get; set; } 
 } 

以及下面的业务对象(也取自链接 - 请注意,我已将传递给 Customer 构造函数的参数从类更改为接口):

public class Customer 
{ 
   public string FirstName { get; private set; } 
   public string LastName { get; private set; } 

   public void ChangeName(IChangeNameCommand command) { 
     FirstName = command.FirstName; 
     LastName = command.LastName; 
   } 
 } 

在我的例子中,命令存储在一个类库中,业务对象存储在其他类库中(因为命令由多个微服务类型项目共享)。如果我遵循指导(并将验证放在命令中),那么我相信没有什么可以阻止开发人员这样做:

public class ChangeNameCommandWithoutValidation : IChangeNameCommand { 
   public string FirstName { get; set; } 
   public string LastName { get; set; } 
 } 

然后将命令(没有验证)传递给域对象。在这种情况下,我相信域对象无法控制传递给它的内容?

因此,我是否应该违反我能找到的所有指导并在域对象中进行验证?我相信我应该这样做,因为这些命令位于与域对象不同的类库中。我是否理解正确?

我相信这个问题在将事件传递给客户域对象时也很重要(使用事件溯源时)。

【问题讨论】:

  • 我认为您可能最好让命令处理程序处理特定命令。然后你可以在那里验证命令,然后调用聚合或实体来执行域逻辑。通过这种方式,您可以拥有一种漂亮而干净的用例方式。我不喜欢将命令直接传递到聚合中,因为它们应该对自己以及它们应该保护的各自实体进行操作等等。但总而言之,这里没有灵丹妙药,请按照您的团队/合作伙伴的决定并可以工作/处理。
  • @kayess,这是否意味着域模型类可能无效(如果开发人员忘记或不小心从命令中删除了验证)?
  • 我不这么认为,您应该在您的 SUT 周围进行单元/集成/其他测试,这将负责确保此类事故不会发生。但正如我最喜欢的一句话所说:“没有针对人类波什的保护措施”。你也可以做验证 AOP 风格,比如有一个装饰器......另一种思考方式,你可以进行代码审查等,但这已经失控,因为我们有点太宽泛了。
  • 我不是 CQRS 方面的专家,但我觉得您使用命令界面很奇怪。一个命令接口有多种实现是否有意义?我认为您的ChangeNameCommandWithoutValidation 示例表明事实并非如此。一个较少的 "evil" 示例是带有中间名的 IChangeNameCommand。您的Customer 实体会接受这样的命令,但实际上并不能真正处理它。
  • 关于您上次的编辑,您没有将事件传递给域对象。您将(如您所做的)命令或其他参数传递给您的域对象,告诉他们做某事。您的域对象或命令处理程序引发由事件处理程序处理的事件...区别是命令 = 现在,事件 = 发生了一些事情,因为与域逻辑有一些交互。

标签: c# domain-driven-design microservices


【解决方案1】:

一些想法:

  • 您链接到的博客文章令人困惑,因为它将验证等同于不变量。

    在 DDD 文献中,不变量大部分时间指的是在根实体中强制执行的域规则,而在其他任何地方都没有。关于这个的有效性,而不是你所说的“所有指导”,是为了在命令方面执行它们——恰恰相反。流行的schools of thought 认为实体应该始终有效,因此应该注意自己的规则(也称为不变量)。

    另一方面,Jimmy Bogard 帖子中的样本所涉及的有效性介于域有效性和用户输入有效性之间。我不会把这种验证放在一边或另一边成为一成不变的规则。虽然认为它确实是 Command 的工作是合理的,但对于某些类型系统,您可以在实体属性类型中完美地编码这种非空约束,如果不利用这种免费的额外正确性将是一种耻辱。

  • 正如 cmets 中所说,将接口置于命令之上似乎很奇怪。

  • 考虑如果有人以恶意方式对命令进行子类化可能会发生什么,这可能是毫无意义的防御。在一个团队中,您可以完全控制实现的内容,我想不出有什么理由让“命令程序员”与“实体程序员”在不同的团队中。

【讨论】:

  • 谢谢。我相信您是说如果尊重其所有不变量,则实体是有效的。我相信您还说,实体可能对以下数据有效:ID=Guid.Empty、Name=""、Age=0 等。对吗?
  • 正如我所说,valid 是一个加载项。对于有效的面向领域的定义,是的。对于面向用户输入的定义,没有。
  • 我刚刚在这里找到了您的答案:stackoverflow.com/questions/30190302/…。我喜欢评论,它说:“您使用验证来确保输入数据的格式有效,然后业务规则决定输入如何/是否更改模型”。我将其解释为在命令中完成了含义验证,并且在域模型中考虑了不变量。 +1 其他答案。
  • 例如,假设客户有一个 Customer.Offer 属性。如果客户年满 21 岁且已花费 1000 英镑,则会创建优惠。在这种情况下,验证(在命令中)将检查年龄是否大于或等于 0,支出是否大于或等于 0,并且域模型将检查客户是否超过 21 岁并且花费了 1000 英镑或更多,然后再分配报价。对吗?
  • 我猜,只要您将这些视为示例,而不是像 "> 0 这样的固定规则,无论上下文如何,始终意味着命令验证" ;-)
猜你喜欢
  • 2015-05-03
  • 2020-03-26
  • 2013-06-27
  • 2012-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-29
相关资源
最近更新 更多