DDD实体封装的核心是通过私有成员变量和受控行为方法保障业务规则,如用_private字段+@property/@setter或private final+public方法实现状态变更校验,聚合根垄断子对象生命周期,不变量检查嵌入方法中,并抛出语义明确的领域异常。

限制类成员变量的可见性(如用 private 或 Python 中的下划线约定)不是装饰技巧,而是 DDD 实体封装的底层防线。它直接支撑“状态变更必须走行为路径”这一核心原则,让业务规则可验证、可审计、可演进。
用私有属性封住直接赋值的后门
DDD 实体的核心不是数据容器,而是行为载体。一旦允许外部直接写 order.status = "paid",就绕过了“已取消订单不可支付”“支付需校验余额”等不变量校验。
- Python 中不提供真正的
private关键字,但应严格使用单下划线前缀(_status)并配合@property+@setter控制写入逻辑 - Java/Kotlin/C# 等语言应明确声明为
private final或private readonly,仅暴露带校验的 public 方法(如confirmPayment()) - 禁止在构造函数外提供公共 setter;所有状态变更入口必须是语义明确的方法名,比如
addItem()而非setItems()
聚合根必须垄断子对象的创建与访问
OrderItem 不是独立存在的对象,它是 Order 聚合内受控的组成部分。它的生命周期、初始化逻辑、状态约束都由聚合根统一管理。
- OrderItem 类不应有 public 构造函数;只允许
Order.add_item(name, price)内部调用其私有构造或工厂方法 - 外部不得持有对 OrderItem 的引用,更不能调用
item.update_price()—— 所有修改必须经由order.update_item_price(id, new_price),确保总价同步更新 - 聚合根内部可用
list[_OrderItem]存储,但对外只暴露不可变视图(如tuple或只读接口),防止外部篡改内部集合
把不变量检查嵌入属性访问与方法执行中
私有字段 + 显式方法的组合,天然构成插入校验、事件发布、审计日志的钩子点。这是贫血模型无法实现的控制力。
- 在
@status.setter中判断当前状态是否允许跳转(如if self._status == "cancelled": raise DomainError("Cannot pay cancelled order")) - 在
add_item()中校验库存、触发ItemAddedEvent、更新_total_amount - 重写
__eq__和__hash__时,只基于不变标识(如_id),不依赖可变字段(如_status),保障实体身份一致性
配合领域异常明确表达业务失败
用私有字段封住状态,不代表隐藏错误。相反,要主动抛出语义清晰的领域异常,让调用方理解“为什么不能做”,而不是“为什么报错”。
- 定义
DomainError及其子类(InvalidStatusTransitionError、InsufficientStockError) - 所有 setter 和行为方法中,校验失败时抛出对应领域异常,而非
ValueError或RuntimeError - 应用层捕获这些异常并转化为用户友好的提示,避免将技术细节泄露到界面











