首页/开发/系统分析与架构设计速查/实体与值对象(Entity / Value Object)
需求分析与建模61 条

实体与值对象(Entity / Value Object)

实体由身份标识区分(订单号相同即同一订单),值对象由属性值区分且不可变(金额、地址、时间区间);值对象不可变可安全共享并简化并发。

实体与值对象

实体由身份标识区分:订单号相同即视为同一订单,即使其他属性全变了仍是同一个对象,它需要表达生命周期与状态变化。值对象由属性值区分且创建后不可变:金额、地址、时间区间、坐标都属于这一类,相等性按全部属性比较。不可变的值对象可以安全共享、自由复制、作为缓存键,也便于把校验规则封装在自身内部。

对比

维度实体值对象
判别依据有生命周期与身份标识只由属性值定义
可变性状态随时间变化创建后不可变,需要变更就创建新值
示例订单、客户、设备、账号金额、坐标、日期区间、联系方式
相等性按标识比较按全部属性比较
持久化有独立标识与主键通常内嵌在实体中,随宿主一起保存

基本信息

值对象要点不可变、自校验、无副作用方法、可安全共享
常见误区把金额做成裸数字或字符串,货币与精度无处表达
标识生成业务标识(订单号)或技术标识(UUID),二者需区分
相等性实现实体比较标识,值对象比较全部字段,不可混用
适用语言Java 用 record 或不可变类,C# 用 record,Python 用 dataclass(frozen=True)

典型使用场景

  • 金额与货币:把数量、货币单位、精度封装在一起,避免单位混用
  • 地址信息:收货地址不可变,修改等于更换新地址,便于审计
  • 时间区间:起止时间成对出现且需要校验先后顺序
  • 编号与编码:订单号、设备编码封装校验规则,杜绝非法值
  • 领域规则收敛:把“金额不能为负”这类校验放进值对象构造
  • 测试简化:值对象相等即可比,断言更直观
值对象不可变意味着“修改”会创建新对象,因此需要注意在高频路径上避免过度分配;多数业务场景下可读性收益远大于开销。
把值对象的字段直接暴露为公共可变属性,等于放弃了自校验与不可变性,规则会被绕过。

易错点

  • 用字符串或裸数字表示金额,货币与精度信息丢失,跨币种计算直接出错
  • 实体与值对象相等性实现混用,集合去重与查找结果不符合业务预期
  • 值对象被设计成可变,共享引用时一处修改影响其他持有者
  • 把校验规则散落在各处而不放进值对象构造,非法值在系统中流转
  • 为每个概念都创建实体,导致大量无身份含义的对象需要持久化

数据来源与参考

  • Eric Evans《Domain-Driven Design》
  • Vaughn Vernon《Implementing Domain-Driven Design》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌