如何在域驱动设计中共享表单和值对象之间的验证?

pro*_*mer 6 forms validation domain-driven-design value-objects

#1.验证表单上的EmailAddress

我有一个后端表单类,其emailAddress属性具有验证逻辑,以便我可以将错误消息返回给用户.我使用类似的方式验证所有表单输入:

$form->fillWith($request->input());

if($form->validate()){
    $form->dispatch($command); // if synchronous, form takes command's messageBag
}

return response($form->getMessageBag()->toJson());
Run Code Online (Sandbox Code Playgroud)

#2.在命令处理程序中验证EmailAddress值对象

我有一个命令处理程序,它将获取原始字符串电子邮件并创建一个值对象.如果电子邮件无效,则value对象将在创建时抛出异常:

public function handle($command){

   try {
      $emailAddress = new ValueObjects\EmailAddress($command->emailAddress);

      // create more value objects...

      // do something else with the domain...

   } catch (DomainException $e) {
        $this->messageBag->add("errors", $e->getMessage());
   } catch (\Exception $e) {
        $this->messageBag->add("errors", "unexpected error");
   }

   return $this->messageBag;
}
Run Code Online (Sandbox Code Playgroud)

在#1中,我希望在发送命令之前尽早捕获验证.但是在#2中,当我构建VO时,重复验证逻辑.

我遇到的问题:

  • 如果我需要更改电子邮件地址的验证要求,那么我必须更新这两个地方.
  • 如果我在表单上使用VO,那么在传递给命令时我将不得不再次解构它们.另外,如果我的表单处于不同的Bounded Context中,那么我将让VO从其他Bounded Context中泄漏域(可能这是必要的吗?).

所以我的问题是,我应该创建一些验证器对象,我的表单验证和VO可以共享/利用吗?或者如何捕获表单和值对象之间的重复验证问题?

the*_*Dmi 6

将验证逻辑封装到可重用的类中.这些类通常称为规范,验证器规则,并且是域的一部分.

有多种方法可以做到这一点,这是我使用的方法:

  1. 定义Specification提供bool IsSatisifed()方法的接口.
  2. 为特定值对象实现此接口,例如EmailWellformedSpec.
  3. 通过使用spec作为前提条件来强制执行域中的业务规则(即,违规始终是编程错误).
  4. 在服务层中使用规范进行输入输入验证(即违规是用户错误).

如果要将多个规格组合成较大的规格,则规范模式是一种很好的方法.请注意,如果使用该模式,则需要通过构造函数传递数据,但这不是问题,因为规范类通常很简单.

  • 我的建议是不要将原语传递给实体方法.传递值对象,并且*通过根据规范*验证传递的VO,使其成为传递VO有效的方法的前提条件. (2认同)
  • @theDmi 我不太明白。您建议通过 VO 和它必须满足的规范并在实体方法中进行检查?如果我有多个方法在这个 VO 上运行,你会在每个方法中都做同样的检查吗?如果这个 VO 只是一个没有任何验证的包装值,那么它与原始值有何不同?你能分享上面的一些代码片段吗? (2认同)