Dmi*_*diu 5 domain-driven-design exception-handling hexagonal-architecture
我正在阅读Vaughn Vernon的书-实现域驱动设计。有一个项目管理应用程序的示例。有聚合,例如BacklogItem,Sprint等。如果我在Domain层中定义了BacklogItemNotFoundException。我的Rest适配器应该抓住它并转换为NotFoundHttpResult吗?还是任何其他破碎的不变异常,例如:EmailPatternBrokenException或TooManyCharactersForNameException或应在Rest适配器(端口和适配器体系结构)中处理的任何东西,然后重新转换为Rest响应?如果是,这意味着RestAdapter应该具有对Domain层的引用?这就是困扰我的...
cho*_*o70 12
这个问题是矛盾的。如果是Domain Exception,说明是域抛出的。
无论如何,域抛出的异常应该由应用层处理。
我有一个用于命令总线的异常处理程序装饰器,它可以捕获任何域异常并将其转换为应用程序异常。
此应用程序异常被抛出到适配器。
适配器知道应用程序异常,而不是域异常。
更新
我的域异常是一个抽象基类,具体域异常从它继承
public abstract class DomainException extends RuntimeException {
private static final long serialVersionUID = 1L;
private ErrorMessage mainErrorMessage;
private List<ErrorMessage> detailErrorMessages;
protected DomainException ( List<ErrorMessage> aDetailMessages, Object... aMainMessageArgs ) {
this.mainErrorMessage = new ErrorMessage(this.getClass().getSimpleName(), aMainMessageArgs );
this.detailErrorMessages = ( (aDetailMessages==null) ? new ArrayList<ErrorMessage>() : aDetailMessages );
}
public ErrorMessage mainErrorMessage() {
return this.mainErrorMessage;
}
public List<ErrorMessage> detailErrorMessages() {
return this.detailErrorMessages;
}
}
Run Code Online (Sandbox Code Playgroud)
ErrorMessage 有一个键和一个参数列表。消息位于属性文件中,其中键是具体域异常类的名称。
应用程序异常只是一种类型,它保存具体的文本消息。
public class ApplicationException extends Exception {
private static final long serialVersionUID = 1L;
private String mainMessage;
private String[] detailMessages = new String[0];
public ApplicationException ( String aMainMessage, Throwable aCause, String... aDetailMessages ) {
super ("Main Message = "+aMainMessage+" - DetailMessages = "+Utils.toString(aDetailMessages), aCause );
this.mainMessage = aMainMessage;
this.detailMessages = ( (aDetailMessages==null) ? (new String[0]) : aDetailMessages );
}
public String mainMessage() {
return this.mainMessage;
}
public boolean hasDetailMessages() {
return (this.detailMessages.length > 0);
}
public String[] detailMessages() {
return this.detailMessages;
}
}
Run Code Online (Sandbox Code Playgroud)
我有一个装饰器(包装每个命令的执行)来处理域异常:
public class DomainExceptionHandlerDecorator extends Decorator {
private final DomainExceptionHandler domainExceptionHandler;
public DomainExceptionHandlerDecorator (DomainExceptionHandler domainExceptionHandler) {
this.domainExceptionHandler = domainExceptionHandler;
}
@Override
public <C extends Command> void decorateCommand(Mediator mediator, C command) throws ApplicationException {
try {
mediator.executeCommand(command);
} catch ( DomainException de ) {
this.domainExceptionHandler.handle (de);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我有一个域异常处理程序,它接受域异常,通过读取属性文件(TextMessageService 完成这项工作)将其转换为应用程序异常并抛出应用程序异常。
public class TranslatorDomainExceptionHandler implements DomainExceptionHandler {
private final TextMessageService configurationService;
public TranslatorDomainExceptionHandler ( TextMessageService aConfigurationService ) {
this.configurationService = aConfigurationService;
}
@Override
public void handle ( DomainException de ) throws ApplicationException {
ErrorMessage mainErrorMessage = de.mainErrorMessage();
List<ErrorMessage> detailErrorMessages = de.detailErrorMessages();
String mainMessage = this.configurationService.mensajeDeError ( mainErrorMessage );
String[] detailMessages = new String [ detailErrorMessages.size() ];
int i = 0;
for ( ErrorMessage aDetailErrorMessage : detailErrorMessages ) {
detailMessages[i] = this.configurationService.mensajeDeError ( aDetailErrorMessage );
i++;
}
throw new ApplicationException ( mainMessage, de, detailMessages);
}
}
Run Code Online (Sandbox Code Playgroud)
适配器(例如 UI)将捕获应用程序异常并向用户显示其消息。但它不知道域异常。
我尽量避免域异常,并且更喜欢使无效状态无法访问。第一个原因是异常是针对特殊的、意想不到的事情,第二个原因是我不喜欢我的代码被细粒度的 try/catch 弄得乱七八糟,因为每个可能会出错的商业小事情。
待办事项未找到异常
对我来说,这通常是您的存储库或查询服务返回 null 或空列表。不需要域例外。
EmailPatternBrokenException
TooManyCharactersForNameException
我让我的 web 框架的验证功能处理这些。你也可以在域中检查它,但它很少会达到那个点,你真的不需要专门处理那种错误。
因此,两个典型的场景是:
+-----------------------+--------------------+-------------------------------------------------+
| Domain | Application | Presentation |
+-----------------------+--------------------+-------------------------------------------------+
| Expected failure case | Return Result.Fail | Clean error message |
+-----------------------+--------------------+-------------------------------------------------+
| Exception | - | Caught in catch-all clause > 500 error or other |
+-----------------------+--------------------+-------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)
我将添加关于错误处理的 2 美分,与 DDD 没有特别的关系。
例外是您向消费者公开的合同的一部分。例如,如果您希望将商品添加到购物车,则您可能会明确抛出的异常包括 itemNotAvailable、shoppingCartNotExisting 等...
另一方面,技术异常不是合同的一部分,它们可能发生但不应明确处理,因为没有人可以对此做任何事情,它们必须暗示操作中断(以及当前工作单元的回滚)。
休息接口是对资源的操作的契约。在 http 上使用 rest 时,合同条款与 http 协议相关。
上面描述的典型操作(添加,即在购物车资源上发布一个项目)将被转换为,例如,404 表示 shopCartNotExisting 和 409 表示 itemNotAvailable(冲突,即资源更新不再可能,因为某些状态在此期间发生了变化)。
所以是的,所有“域”异常(作为合同一部分的预期异常)都应该由其余适配器显式映射,所有未经检查的异常都应该导致 500 错误。
| 归档时间: |
|
| 查看次数: |
1699 次 |
| 最近记录: |