Error handling 在域驱动的设计实体和聚合根中,有什么方法来处理和返回错误(非异常和异常)?

Error handling 在域驱动的设计实体和聚合根中,有什么方法来处理和返回错误(非异常和异常)?,error-handling,exception-handling,domain-driven-design,Error Handling,Exception Handling,Domain Driven Design,我试图找到一篇很好的文章/示例,介绍DDD实体如何处理错误(哪些被视为异常错误,哪些不被视为异常错误),以及它们如何将错误传递给调用应用程序层(通常将操作包装在需要回滚的事务中) 目前,我正在考虑考虑将所有事务(如验证)中断为异常的所有错误。这样,我可以在“catch”块中回滚事务。例如: SomeApplicationService: //在此处启动事务 // ... 尝试 { $user=$userRepository->userOfId($id); $user->doSomething()

我试图找到一篇很好的文章/示例,介绍DDD实体如何处理错误(哪些被视为异常错误,哪些不被视为异常错误),以及它们如何将错误传递给调用应用程序层(通常将操作包装在需要回滚的事务中)

目前,我正在考虑考虑将所有事务(如验证)中断为异常的所有错误。这样,我可以在“catch”块中回滚事务。例如:

SomeApplicationService:

//在此处启动事务
// ...
尝试
{
$user=$userRepository->userOfId($id);
$user->doSomething();
$user->doSomethingElse();//保存($user);
} 
捕获(自定义适用于UIException$e)
{
//自定义友好的用户界面错误
//回滚事务并将错误/消息添加到UI有效负载DTO
// ...
}
捕获(另一个自定义例外$e)
{
//对UI不友好,因此使用UI的常规错误消息
//回滚事务并将错误/消息添加到UI有效负载DTO
// ...
}
捕获(例外$e)
{
//捕获所有其他异常,对UI使用常规错误消息
//回滚事务并将错误/消息添加到UI有效负载DTO
// ...
}
//结束交易

这是正确的方法,还是我遗漏了什么?

通常有两种类型的错误:

  • 具有业务意义的业务错误。例如,
    StockFullError
    ProductNotAvailableError
    ,等等。这些错误是预期的,因为它们是在代码中显式创建的

    可以使用异常或使用 函数方式:显式显示返回类型中的错误。对于 实例:

    异常使代码更简单。函数式编程的错误处理方式使代码更容易推理,也更容易预测。我建议后者

  • 基础结构错误是链接到数据库、网络、外部服务等的错误。这些错误通常是意外的

错误发生了。当错误发生时,您不希望应用程序处于不一致的状态。例如,您希望避免将聚合的一半存储在数据存储中

聚合完整性 这里的关键问题是维护聚合的完整性。关键点是何时必须在数据存储中写入聚合。我可以看到两种情况:

持久化聚合的操作是原子的 只要您的数据存储能够提供原子操作,就不必担心数据的完整性,因为原子操作的结果要么失败,要么成功。没有部分写入

这意味着您可以让您的http层处理异常并返回500(例如)

持久化聚合的操作不是原子操作 有时,确保原子性是不可能的

多个表中的一个聚合 如果需要将聚合存储在多个表中,有一些解决方案:

  • 交易。围绕事务中的代码可能是一个解决方案。然而,交易也有一些缺点。如果计算太长或调用这部分代码太频繁,则会降低应用程序的速度
  • 这是一种古老的模式。其思想是注册您想要执行的操作:添加、更新、删除等。最终,UOW使用事务将更改应用于数据库。有关更多信息,请参阅福勒的文章
传奇:有时候一笔大交易是不可能的 当您发送电子邮件并在数据库中保存某些内容时,您不能将该电子邮件包含在事务中

在这种情况下,你可以使用传说。这个想法是,有时候,你不能有一个单一的大事务来强制原子性。然而,通常很容易进行几笔小交易

传奇的理念是将每一笔交易与一笔补偿交易联系起来。例如,给定“交易”:发送电子邮件确认产品已购买,补偿“交易”可以发送电子邮件以优惠券道歉


每个事务都会运行小事务。如果其中一个失败,则运行补偿事务。最终,这使我们能够获得原子性

这可能会有帮助:@plalx这是一个很好的链接/答案!谢谢你…我现在正在通读。。。
// start transaction here
// ...

try 
{
    $user = $userRepository->userOfId($id);
    $user->doSomething();
    $user->doSomethingElse();  // <-- imagine an error thrown here
    $userRepository->save($user);
} 
catch (CustomFriendlyForUIException $e)
{
    // Custom Friendly for UI error
    // Rollback transaction and add error/message to UI payload DTO
    // ...
}
catch (AnotherCustomException $e)
{
    // Not friendly to UI, so use general error message for UI
    // Rollback transaction and add error/message to UI payload DTO
    // ...
}
catch (Exception $e)
{
    // Catch all other exceptions, use general error message for UI
    // Rollback transaction and add error/message to UI payload DTO
    // ...
}

// end transaction