DDD中访问修饰符是建模意图的强制表达工具:聚合根public、内部实体default、字段private、状态sealed,结合包隔离与工程规范实现严格边界控制。

在DDD中,访问修饰符不是语法装饰,而是建模意图的强制表达工具。聚合根与内部实体的隔离,核心目标是让外部代码只能通过聚合根操作整个聚合,不能绕过它直接构造、修改或引用内部对象。Java的访问控制机制恰好能支撑这一设计契约。
用default(包级私有)锁死聚合内部对象的创建入口
将聚合内除聚合根外的所有实体、值对象定义为default访问级别,是最轻量也最有效的隔离手段。只要把整个聚合放在一个独立package里(如com.example.order),内部类的默认构造函数就天然无法被包外调用。
-
聚合根:声明为
public,提供带业务校验的构造方法和工厂方法 - 内部实体(如OrderLine):构造函数不加任何修饰符,仅允许同包类(即Order聚合根)调用
-
效果:外部模块连
new OrderLine(...)都编译不过,彻底杜绝非法实例化
用private封禁实体字段的直接暴露
聚合内部实体之间可以互相访问,但对外必须隐藏状态细节。即使是在同包内,也不应让OrderLine的price字段被Order随意读写——这会破坏封装边界。
- 所有字段声明为
private,只通过受控方法暴露必要行为(如applyDiscount()) - 避免getter/setter泛滥;若需读取,提供语义化方法(如
getTotalAmount()而非getPrice()) - 这样既守住聚合内聚性,又为未来重构(如价格拆分为单价+数量)留出余地
用sealed限制聚合扩展方向(Java 15+)
当聚合需要支持多种变体(如不同类型的订单状态),又不想开放任意继承时,sealed是比抽象类更精准的选择。
立即学习“Java免费学习笔记(深入)”;
- 定义
sealed abstract class OrderStatus,明确列出permits Created, Paid, Shipped - 每个子类用
final或non-sealed注明是否允许进一步扩展 - 配合模式匹配(
switch (status)),编译器可确保所有状态都被处理,避免遗漏分支
用file修饰符(C#场景)或package-private组合强化边界意识
虽然Java没有file修饰符,但可通过命名与结构强化“聚合即单元”的认知:
- 每个聚合独占一个package,不混入其他领域概念
- 包名体现业务语义(如
com.example.shipping而非com.example.entity) - 配合IDE设置,禁止跨包引用,把访问控制从语言层延伸到工程规范层



















