用封装替代直接访问属性是解决业务数据不一致的根本办法,即设属性为私有,通过含校验与业务逻辑的公共方法或属性访问,如订单Cancel()检查状态、年龄由SetBirthDate自动计算并触发提醒。

用封装替代直接访问属性,是解决业务数据不一致问题最根本的办法。
把属性设为私有,只通过方法操作
公开字段(如 public string Name;)会让外部代码随意读写,绕过业务规则。应改为私有字段 + 公共属性或方法,把校验、联动、状态更新等逻辑收拢到内部。
- 例如:订单状态不能从“已支付”直接改成“已取消”,需调用 Cancel() 方法,其中检查是否超时、是否已发货、是否退款中
- 再如:用户年龄不应允许直接赋值,而应在 SetBirthDate(DateTime date) 中自动计算并触发生日提醒逻辑
在 setter 中嵌入业务约束
即使使用自动属性,也建议用完整属性语法,在 set 块中加入校验和副作用处理,避免“设了就完事”。
- 禁止无效值:if (value
- 保持关联一致性:修改 Price 时,自动重算 TotalAmount 并标记为“已变更”
- 触发领域事件:如 IsVerified = true 后,发布 UserVerifiedEvent 供其他模块响应
用只读属性 + 工厂/构建器控制创建入口
对关键聚合根(如订单、合同),禁止外部 new,改用静态工厂方法或构建器模式,确保对象一创建就满足业务完整性。
- 构造函数不做复杂逻辑,把校验和初始化移到 CreateOrder(customer, items) 中统一处理
- 构建器可分步验证:添加商品时检查库存,提交前校验地址格式、支付方式兼容性
- 创建后返回只读接口(如 IReadOnlyOrder),防止后续意外修改核心字段
配合领域事件同步衍生状态
当一个变更需要多处响应(如修改价格要更新缓存、通知下游、记录日志),不要让调用方手动逐个处理——在领域内发出事件,由订阅者各司其职。
- 避免“改完 Price 又手动调 UpdateCache()、SendPriceChangedNotice()”,易遗漏或顺序错乱
- 事件由领域对象内部触发(如 OnPriceChanged(oldPrice, newPrice)),保证与主逻辑原子性绑定
- 事件处理器可异步执行,不影响主流程,但确保最终一致性

















