用密封类体系构建DDD代数数据类型,核心是通过sealed interface定义领域契约、record实现不可变变体、switch强制穷尽处理,使业务概念“有限、封闭、不可变”由编译器保障,天然形成防腐层。

用密封类体系构建 DDD 中的代数数据类型,核心是把“有限、封闭、不可变”的业务概念直接映射到类型系统里——不是靠文档约定,而是让编译器帮你守住边界。
用 sealed interface 定义领域契约
选接口而非抽象类,语义更清晰:它不承载行为实现,只声明“这个领域概念有哪些合法形态”。比如订单状态:
- 声明为 public sealed interface OrderStatus permits Placed, Confirmed, Shipped, Cancelled {}
- 所有变体必须在 同一模块内可见(默认模块下建议同文件)
- 不能省略 permits 子句,否则编译失败
用 record 实现具体、不可变的变体
每个变体对应一个业务事实,天然适合 record:
- public record Placed(Instant placedAt, String orderRef) implements OrderStatus {}
- public record Confirmed(Instant confirmedAt, String paymentId) implements OrderStatus {}
- record 自动提供不可变字段、构造器、equals/hashCode/toString,无需手写样板
- 显式加 final(虽 record 默认 final,但写出来更明确、可读性更强)
用 switch 表达式强制穷尽处理
消费端逻辑不再依赖 instanceof 或 if-else,而是由编译器保障覆盖全部合法状态:
- 写法:return switch (status) { case Placed p -> "已下单"; case Confirmed c -> "已确认"; ... };
- 漏掉任一变体,或某个实现类不是 final,编译直接报错
- 禁止加 default 分支——这逼你直面“是否真的理解了所有业务状态”
跨限界上下文协作时自动形成防腐层
当订单状态要传给物流上下文,密封体系天然支持 ACL(防腐层)设计:
- 物流服务只接收 OrderStatus,不关心具体实现类名
- 但它必须显式处理每种状态,无法忽略 Shipped 或假装“兼容未来类型”
- record 的不可变性防止状态在传递中被意外修改
- 若其他包试图新增 class FraudState implements OrderStatus,编译直接拦截
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











