不变量,验证和DRY原则

时间:2014-03-20 07:04:52

标签: validation domain-driven-design dry invariants

我目前正在尝试消化与DDD中的不变量和验证相关的信息。如果我得到正确的验证不是域的关注,应该在外面完成,以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中。

令我困惑的是两件事:

  • 如何区分业务规则(不变量)和验证
  • 如何尊重DRY原则

让我详细说明一下。考虑我们有一个涵盖招标的域模型。两个主要参与者是招标流程组织者(组织者)和招标流程参与者(参与者)。 主办单位发布招标公告,其中包含有关舞台条款和要求(例如起始最高价格)的信息。 投标是一个由多个阶段组成的过程。每个阶段都有自己的条款。第一个阶段是“征集出价”。在阶段期间,参与者可以发送其优惠(提案)。

有两个基本要求:

  1. 提案价格必须低于起始最高价格
  2. 参与者只能在“致电”期间提交报价 出价“舞台
  3. 从技术上讲,我们可以像这样实现它(省略细节):

    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();
       }
    }
    

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

    非常感谢。

    更新

    我想我不够清楚。简而言之,我有两件事需要考虑:

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

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

      如果我错了,请纠正我。

1 个答案:

答案 0 :(得分:1)

尽管我已经写了很多DDD代码,但坦白地说我仍然不确定术语,我不确定是否有社区共识。我基本上已经停止使用DDD的行话,发现我没有像你正在摆出的那样啃着很多问题。

另一种使用实际术语陈述问题的方法是......

  

出价较低的人出价会让你感到沮丧   用户,以及对已结束拍卖的出价。所以我们需要确保不会发生这种情况。

阅读数据时的验证

当您显示用户输入出价的屏幕时,您当然会对用户进行一些验证,即出价必须大于之前的出价(例如通过jQuery)并且出价只能在CallForBids阶段被接受(例如,仅显示表格。

您必须进行此验证,否则您将为用户提供非常糟糕的体验 - 允许他们输入出价仅被告知拍卖已结束。所以我们知道你在阅读数据时必须以某种方式表达这些规则。然而,关键是这个:

  

您不能保证您在屏幕上显示的内容基于此   A时的信息在用户发布时为真   执行在时间B写入数据的操作。

所以这里的验证不一定是密不透风的。不要那么多出汗。即使您通过复制逻辑搞砸了,我们也无法100%保证写入仍会通过。

撰写数据时的验证

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

  

在编写数据和时,您必须检查业务规则   您必须在同一交易中执行此操作,否则您不能   保证一致性。

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

那么这对你意味着什么?

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

希望能够解决它。