DomainException 是领域层专用异常,需命名体现业务意图、继承 RuntimeException、构造简洁无栈追踪、禁止跨层传播和包装底层异常。

在DDD中,DomainException 是领域层专用的异常类型,用于表达业务规则被违反、不变量被破坏等**领域语义明确的错误**,它不该是技术异常(如 NullPointerException),也不该暴露基础设施细节(如数据库连接失败)。自定义的关键在于:**命名体现领域意图、构造简洁、不带栈追踪干扰、禁止跨层传播**。
命名要体现业务含义
避免泛泛叫 DomainException 或 BusinessException。应按具体场景命名,让调用方一眼理解发生了什么:
-
✅ 推荐:
InsufficientBalanceException、InvalidOrderStatusTransitionException、ProductOutOfStockException -
❌ 避免:
DomainException(太泛)、ValidationException(属于应用层或DTO校验)、DatabaseException(泄露基础设施)
继承 RuntimeException,无检查异常
领域异常是“意料之中但不可恢复”的业务失败,不应强制上层 try-catch。直接继承 RuntimeException 即可,无需实现序列化接口(除非有特殊分布式需求):
public class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String accountNumber, BigDecimal required) {
super(String.format("Account %s has insufficient balance. Required: %s",
accountNumber, required));
}
}
构造器只接收业务相关参数(如账户号、金额),不传 Throwable cause —— 领域异常通常不包装底层异常,否则会污染领域语义。
立即学习“Java免费学习笔记(深入)”;
禁止在领域对象中 throw new XxxException()
领域对象(如 Aggregate Root)抛出异常时,应使用**已有定义的领域异常类**,而不是 new RuntimeException 或字符串拼接。例如:
-
✅ 正确:
if (balance.compareTo(amount) -
❌ 错误:
throw new RuntimeException("余额不足");或throw new IllegalArgumentException("...");
这保证了异常类型可被上层(如 Application Service)精准捕获并做统一处理(如返回用户友好的提示、触发补偿逻辑)。
不包含日志、监控或事务控制逻辑
DomainException 本身只是“信号”,不负责记录日志、打点监控、回滚事务。这些职责属于外层(Application Layer 或 Infrastructure Layer):
- 应用服务捕获
InsufficientBalanceException后,可记录业务日志、调用通知服务、返回特定错误码 - 框架(如 Spring)可通过
@ExceptionHandler统一转换为 HTTP 响应,但异常本身不耦合 Web 层 - 不要在异常类里写
log.error(...)或TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()



















