Validation 不变量、验证和干燥原理

Validation 不变量、验证和干燥原理,validation,domain-driven-design,dry,invariants,Validation,Domain Driven Design,Dry,Invariants,我目前正试图在DDD中消化与不变量和验证相关的信息。如果我理解正确,验证不是域的问题,应该在外部进行,以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中 让我困惑的其实是两件事: 如何区分业务规则(不变量)和验证 如何尊重干燥原则 让我详细说明一下。考虑到我们有一个覆盖标号的域模型。两个主要参与者是投标过程组织者(组织者)和投标过程参与者(参与者)。组织者发布一份招标公告,其中包含有关阶段条款和要求(例如起始最高价格)的信息投标是一个由几个阶段组成的过程。每个阶段都有自己的

我目前正试图在DDD中消化与不变量和验证相关的信息。如果我理解正确,验证不是域的问题,应该在外部进行,以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中

让我困惑的其实是两件事:

  • 如何区分业务规则(不变量)和验证
  • 如何尊重干燥原则
让我详细说明一下。考虑到我们有一个覆盖<强>标号的域模型。两个主要参与者是投标过程组织者(组织者)和投标过程参与者(参与者)。组织者发布一份招标公告,其中包含有关阶段条款和要求(例如起始最高价格)的信息投标是一个由几个阶段组成的过程。每个阶段都有自己的术语。第一阶段是“招标”。在此阶段允许参与者发送其报价(提案

有两个基本要求:

  • 建议价格必须小于起始最高价格
  • 参与者只能在“邀请”期间提交报价 “投标”阶段
  • 技术上我们可以这样实施(细节省略):

    考虑到上面提到的一切,在给定的上下文中,不变量和验证之间的细微差别是什么?验证程序和聚合中的代码是否应该重复?(我不想将验证器注入实体)

    多谢各位

    更新

    我想我还不够清楚。长话短说,我有两件事要考虑:

  • 业务规则和不变量之间的区别
  • 坚持干法违反SRP,反之亦然
  • 我和另一位开发人员最近进行了讨论,我们得出的结论如下:

    • 不变量是一些必须独立于规则而遵守的规则 商业规则。代码可能是相同的,即使在概念上是相同的 两件不同的事情
    • 在这种情况下,可能会违反DRY原则,以便 遵守SRP原则

    如果我错了,请纠正我。

    尽管我写了很多DDD代码,但坦率地说,我仍然不确定术语,也不确定是否有社区共识。我基本上已经不再使用DDD的行话了,我发现像你提出的问题那样让人头疼的问题要少得多

    所以,用实用术语来说明问题的另一种方法是

    被出价较低的人出价过高会让你很生气 用户,就像在封闭拍卖中出价一样。所以我们需要确保这不会发生

    读取数据时的验证

    当您显示屏幕供用户输入出价时,您当然会向用户确认出价必须大于上一次出价(例如通过jQuery),并且只有在
    callbanks
    阶段(例如仅显示表单)才能接受出价

    你必须进行此验证,否则会给用户带来非常糟糕的体验-允许他们在得知拍卖结束后才进行竞价。因此我们知道,在阅读数据时,你必须以某种方式表达这些规则。然而,关键是:

    您不能保证屏幕上显示的任何内容都是基于 在用户访问时,A时的信息将是真实的 执行在时间B写入数据的操作

    因此,这里的验证不必是密封的。别担心。即使你复制逻辑搞砸了,我们也不能100%保证写操作会通过

    写入数据时的验证

    屏幕上的数据变得陈旧,正如我们在上面所观察到的:用户可能在拍卖结束后输入了出价,或者自数据显示在屏幕上以来,最低出价可能已经上升。因此,为了避免系统状态违反业务规则(因此不可靠且不完整)

    在写入数据时,您必须对照业务规则进行检查 您必须在同一事务中执行此操作
    ,否则无法执行 保证一致性

    (也有最终的一致性,但这是一个完全不同的蜡球,超出了这个答案的范围。)

    那对你来说意味着什么

    • 您的命令处理程序是聚合/事务边界/本周人们管它叫什么
    • 如果这两条规则中的任何一条被破坏,该命令将无法成功(应引发异常)。不应更改任何状态
    • 应该假设该命令成功。也就是说,从您的MVC控制器中,不要为失败的命令添加任何特殊的错误处理。命令失败非常罕见,但它会不时发生
    • 任何内部实现都不重要:如果您想要有两个类,一个用于执行每个规则,请直接执行。只要规则在单个事务中执行,这对业务来说并不重要。这就是您应该关注的
    • 这也是您应该测试的内容-只有当命令的参数相对于系统状态无效时才会引发异常(只有当命令发布将触发其他处理程序的事件时,您才会测试成功。)

    希望这能澄清问题。

    欢迎来到StackOverflow!这是一个好问题,但我认为需要对它进行一些改进,然后才能成为一个好问题
    class SubmitHandler
    {
    
        /**
        * Send proposal
        *
        * @param SubmitCommand $command
        */
       public function execute($command)
       {
           $this->isReadyToBeSend($command);
    
           $participant = $this->participantRepository->find($command->id);
           $participant->submitProposal();
    
       }
    
       private function isReadyToBeSend($command)
       {
            $result = $this->validate($command);
            if (!$result->isValid()) {
                throw new ProposalException($result->getMessages()[0]->getMessage());
            }
       }
    
       public function validate($command)
       {
           // Here we check if starting price is provided 
           // and it is less than starting maximum price 
           // as well as the Call for bids Stage is still active 
           // so that we are allowed to submit proposals
    
           return Validator::validateForSending($command);
       }
    
       public function canBeExecuted($command)
       {
           return $this->validate($command)->isValid();
       }
    }
    
    // In the UI we send command to the handler
    $commandHandler->handle($submitCommand);
    
    
    class Participant extends AggregateRoot
    {
       public function submitProposal()
       {
          // here we must enforce the invariants
          // but the code seems to be almost the same as
          // in the validator in the Command Handler
          $this->isReadyToBeSent();
       }
    
       // throws exceptions if invariants are broken
       private function isReadyToBeSent()
       {
           $this->isPriceCorrect();
           $this->AreTermsCorrect();
       }
    }