DDD中自定义异常是表达领域规则、保护完整性的核心手段,需用明确领域术语命名、在聚合根内抛出、携带上下文信息,并按语义区分checked/unchecked,配合事件与防腐层实现边界防护。

在领域驱动设计(DDD)中,自定义异常不是技术细节的堆砌,而是表达领域规则、保护领域完整性的重要手段。它让错误语义显性化、可读、可捕获、可响应——本质上,是把“什么不能发生”写进领域语言里。
用领域术语命名异常,而非技术原因
避免 InvalidOrderStateException 这类混合了技术(state)和模糊概念(invalid)的命名;改用 OrderAlreadyShippedException 或 InsufficientInventoryException。这些名称直接来自领域模型中的事实,业务人员能看懂,开发者一眼明白违反了哪条业务约束。
- 异常名 = 领域中一个明确的、不可接受的状态或动作
- 不包含 “Validation”、“Error”、“Exception” 等冗余后缀(Java 类型已表明它是异常)
- 保持与聚合根、值对象、领域服务中使用的术语完全一致
在聚合根内部抛出,封装不变量检查
聚合根是领域规则的守门人。所有关键业务约束应在它的方法中主动验证并抛出对应异常,而不是等到应用层或基础设施层才发现问题。
- 例如,在 Order.confirm() 中检查库存是否充足,不足则抛出 InsufficientInventoryException
- 在 Account.withdraw(amount) 中检查余额,余额不足抛出 InsufficientBalanceException
- 异常应携带上下文信息(如订单号、账户ID、缺失数量),方便日志追踪和前端提示
区分 checked 与 unchecked,按语义而非习惯
DDD 不强制使用 checked 异常,但建议:若该异常代表调用方**本应预判并规避**的领域违规(如重复提交、状态非法流转),用 unchecked(RuntimeException 子类);若代表**必须显式处理的可恢复业务分支**(如支付渠道临时不可用),可考虑 checked 异常,但需谨慎——多数领域异常本质是“失败即中断”,不应鼓励静默吞掉。
立即学习“Java免费学习笔记(深入)”;
- OrderAlreadyConfirmedException → unchecked:调用方不该在已确认状态下再调 confirm()
- PaymentGatewayUnavailableException → 可选 checked:属于外部协作失败,可能需降级或重试
- 避免为“校验失败”统一用 checked,这会污染领域逻辑,让应用层写满 try-catch
配合领域事件与防腐层做边界防护
自定义异常主要在领域层内使用,但它的影响要延伸到边界:
- 应用服务捕获领域异常后,可发布 OrderConfirmationFailed 领域事件,供其他限界上下文响应
- 在防腐层(ACL)对接外部系统时,将对方返回的错误码/消息,翻译为本上下文的领域异常(如把支付网关的 “ERR_BALANCE_LOW” 映射为 InsufficientBalanceException)
- API 层(如 Spring MVC)统一拦截这些异常,转为标准 HTTP 状态码与业务友好的 error payload(如 400 Bad Request + {"code":"INSUFFICIENT_INVENTORY","order_id":"ORD-123"})



















