在域驱动设计实体和聚合根中,有什么方法可以处理和返回错误(非异常和例外)? [英] What approach is there for handling and returning errors (non-exceptional and exceptional) in Domain Driven Design entities and aggregate roots?

查看:46
本文介绍了在域驱动设计实体和聚合根中,有什么方法可以处理和返回错误(非异常和例外)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找到一篇不错的文章/示例,介绍DDD实体如何处理错误(以及什么被认为是异常错误,哪些不会被视为错误)以及它们如何将它们传递给调用应用程序层(通常将操作包装)在需要回滚的交易中).

I'm trying to find a good article/examples of how DDD entities treat errors (and what would be considered exceptional errors and what wouldn't) and how they pass them up to the calling application layer (which usually wraps operations in a transaction that would need to be rolled back).

目前,我正在考虑将可能破坏聚合交易(例如验证)的所有错误视为例外.这样,我可以在"catch"块中回滚事务.例如:

Currently I'm thinking to consider all errors that would break the transaction of an aggregate (such as validation) to be exceptions. This way I can rollback the transaction in a "catch" block. For example:

SomeApplicationService:

// 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

这是正确的方法,还是我遗漏了一些东西?

Is this the correct approach, or am I missing something?

推荐答案

通常,有两种错误类型:

Usually, there are two types of error:

  • 具有业务含义的业务错误.例如, StockFullError ProductNotAvailableError 等.由于这些错误是在代码中显式创建的,所以会出现这些错误.

  • business errors that have a business meaning. For instance, StockFullError, ProductNotAvailableError, etc. These errors are expected because they are explicitly created in the code.

可以使用异常或使用功能方式:在返回类型中显式显示错误.为了实例: Ether< Error,T> .

These kind of error can be implemented using exceptions or using the functional way: explicitly show errors in the return type. For instance: Either<Error, T>.

异常使代码更简单.使用功能性编程方式处理错误使代码更易于推理和更可预测.我会建议后者.

Exception make the code simpler. Handling error the functional programming way make the code easier to reason about and more predictable. I would advise the latter.

基础结构错误是与数据库,网络,外部服务等相关的错误.这些错误通常是意外的.

infrastructure errors are errors linked to the database, network, external services, etc. These error are usually unexpected.

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

Error happen. And when error happen, you don't want the application to be in an inconsistent state. For instance, you want to avoid to have half of the aggregate stored in the datastore.

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

The key problem here is to maintain the aggregate integrity. The critical point is when the aggregate has to be written in the datastore. I can see two situations:

一旦您的数据存储区可以提供原子操作,就不必担心数据完整性,因为原子操作的结果是失败还是成功.没有部分写入.

As soon as your datastore can provide atomic operations, there is no worry to have about the data integrity because the result of an atomic operation is either a failure or a success. There is no partial writes.

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

That means that you can just let your http layer handle exception and return 500 for instance.

有时,无法确保原子性.

Sometimes, ensuring atomicity is not possible.

如果您需要将汇总存储在多个表中,则有一些解决方案:

If you need to store your aggregate in several tables, there are some solutions:

  • 交易.将代码包含在事务中可能是一个解决方案.但是,交易有一些缺点.如果计算时间太长,或者如果这部分代码调用得太频繁,则会降低应用程序的速度.
  • 工作单元是一种旧模式.想法是注册您想要执行的操作:添加,更新,删除等.最终,UOW使用事务将更改应用于数据库.有关更多信息,请参见Fowler的文章.
  • Transactions. Surrounding the code in a transaction could be a solution. However transactions have some disadvantages. If the computation is too long or if this part of the code is called too often, it would slow down the application.
  • Unit Of Work is an old pattern. The idea is to register operations that you want to do: add, update, delete, etc. Eventually, UOW applies the changes to the database using a transaction. See Fowler's article for more information.

发送电子邮件并将某些内容保存在数据库中时,您不能在交易中包括该电子邮件.

When you send an email and save something in the database, you can't include the email in the transaction.

在这种情况下,您可以使用SAGA.这个想法是,有时候,您无法通过一个大事务来强制执行原子性.但是,通常很容易进行几笔小交易.

In that case, you can use SAGAs. The idea is that, sometime, you can't have one single big transaction to enforce atomicity. However, it is usually easy to have several small transactions.

SAGA的想法是将每笔交易与补偿交易相关联.例如,给定交易":发送​​电子邮件以确认该产品已购买,则可以向补偿性交易"发送电子邮件以对优惠券表示歉意.

The idea of a SAGA is to associate every single transaction to a compensating transaction. For instance given the "transaction": send an email to confirm that the product is bought, the compensating "transaction" could be sent an email to apologise with a coupon.

每个事务小事务都运行.如果其中之一失败,则运行补偿事务.最终,这可以实现原子性.

Every transaction small transaction is run. If one of these fails, compensating transaction are run. Eventually, this enables to get atomicity.

这篇关于在域驱动设计实体和聚合根中,有什么方法可以处理和返回错误(非异常和例外)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆