教程 ddd tutorial

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

在领域驱动设计(DDD)中,实体(Entity) 和 值对象(Value Object) 是构建领域模型的两种核心元素。理解它们的区别和使用场景,能够帮助开发人员更加准确地建模业务需求。

实体(Entity)

定义

实体是具有唯一标识的对象,它们在系统的生命周期中可能会经历状态变化。实体的生命周期不依赖于它的属性,而是由它的唯一标识符(ID)决定。

特征

  • 唯一标识符(ID):实体通过唯一的标识符来区分其他实体,即使它的属性发生变化,其标识符仍然保持不变。
  • 生命周期:实体有自己的生命周期,通常由创建、变更和删除组成。
  • 可变性:实体的状态是可变的,可以通过操作方法或领域事件进行修改。

示例

java
public class Customer {
    private final String customerId;
    private String name;
    private String email;

    public Customer(String customerId, String name, String email) {
        this.customerId = customerId;
        this.name = name;
        this.email = email;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void updateEmail(String newEmail) {
        this.email = newEmail;
    }
}

何时使用实体?

当你需要跟踪一个对象的身份,并且该对象的属性可能会发生变化时,使用实体。例如,客户、订单、用户等,都是具有唯一标识的实体。

值对象(Value Object)

定义

值对象没有唯一标识符,它们仅通过属性值来区分。值对象通常是不可变的,创建后不能更改其状态。值对象是描述对象属性的一种方式,可以通过其属性来定义其值。

特征

  • 无唯一标识符:值对象没有唯一标识符,它们的身份由其属性的值决定。
  • 不可变性:值对象创建后,其属性值不能被修改。任何修改值对象的操作,都会返回一个新的值对象。
  • 平等性:值对象的平等性通常是通过其属性值来判断的。

示例

java
public class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currencies must match.");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
}

何时使用值对象?

当你需要描述对象的某些属性而不关心对象的身份时,使用值对象。例如,地址、货币金额、日期区间等,都是典型的值对象。

实体与值对象的区别

特性 实体(Entity) 值对象(Value Object)
唯一标识符 有唯一标识符(ID) 无唯一标识符
可变性 状态可变 状态不可变
平等性 通过标识符判断平等 通过属性值判断平等
适用场景 表示具有身份的对象,如客户、订单 表示描述性信息,如货币、地址、日期区间

实践建议

  • 在设计领域模型时,如果对象具有唯一身份,并且在系统中有生命周期,应该设计为实体。
  • 如果对象只是描述某些属性,且没有必要追踪身份,应该设计为值对象。
  • 避免过度使用实体,尽可能将不需要身份标识的对象建模为值对象,以简化系统设计。

通过合理使用实体与值对象,你可以构建出更加简洁且具备高内聚、低耦合的领域模型。