DomainException是DDD中表达业务规则违反的unchecked异常,继承RuntimeException,需按限界上下文定义具体子类,构造函数体现业务语义,仅在领域对象关键行为中主动抛出。

在DDD中,DomainException 是领域层专用的、表达业务规则被违反或领域不变量被破坏的异常类型,它不描述技术故障,而反映领域语义上的失败(比如“余额不足”“订单已取消无法再支付”)。它的设计目标是:清晰传达领域意图、避免被上层随意捕获吞没、支持统一错误处理,同时与基础设施异常(如数据库连接失败)严格隔离。
DomainException 应该继承 RuntimeException
DDD强调领域逻辑的简洁性与可读性,强制检查型异常(checked exception)会污染领域方法签名,迫使调用方处理与业务无关的技术细节。因此:
- 定义为 unchecked 异常(即继承
RuntimeException),让领域方法保持“只关注业务”的干净接口 - 不建议继承
Exception或声明throws,否则违背领域模型的内聚原则 - 典型写法:
public class InsufficientBalanceException extends RuntimeException
构造函数应体现领域语义,而非技术信息
DomainException 的消息和参数要直接对应业务场景,便于日志追踪和前端提示:
- 提供含业务上下文的构造函数,例如:
InsufficientBalanceException(String accountId, BigDecimal required) - 重写
getMessage()返回用户/运维可理解的表述,如"账户 " + accountId + " 余额不足,需 " + required - 避免出现 “NullPointerException”“SQLSyntaxErrorException” 等技术词汇
- 可额外封装领域相关属性(如失败的聚合根ID、违规值),供上层做精细化处理
按业务能力分组定义,而非按技术层次泛化
不要定义一个万能的 DomainException 类再靠 message 区分;而应围绕限界上下文和聚合,定义具体、不可替代的异常子类:
立即学习“Java免费学习笔记(深入)”;
- 每个异常类名应是名词短语,明确表达“什么业务规则被破坏”,如
OrderAlreadyShippedException、ProductOutOfStockException - 同一限界上下文内,异常粒度由业务重要性决定:关键约束(如资金一致性)需独立异常类;次要校验(如昵称长度)可用通用校验异常(如
DomainValidationException)配合错误码 - 不同限界上下文之间异常类不共享,避免耦合——订单域的异常不应被库存域直接引用
在领域对象内部主动抛出,且仅在验证失败时触发
DomainException 应出现在聚合根、实体或值对象的关键行为方法中,作为业务规则守门人:
- 在执行动作前校验前提条件(precondition),不满足则立即抛出,例如:
withdraw(amount)中检查if (balance.compareTo(amount) - 不在 Repository、Application Service 或 Controller 中抛出 DomainException —— 那里应抛出应用层或基础设施异常
- 禁止用异常控制正常流程(如用异常实现“查找不存在则创建”),这违背异常的设计本意



















