具有标识的值对象

时间:2012-04-12 09:42:37

标签: domain-driven-design

在应用程序中,我们将Company建模为实体,将Address建模为值对象:

public class Company : Entity  {
    public Address PrimaryAddress { get; set; }
    public Address SecondaryAddress { get; set; }
}

public class Address : ValueObject {
    public string ZipCode { get; private set; } // etc.

    public Address(string zipCode) {
        ZipCode = zipCode;
    }
}

在这里,Address没有身份并且是不可变的。要更新公司的PrimaryAddress,请将其替换为新的Address对象。

然而,在进一步发现域名后,我们发现公司可能拥有可变数量的地址。将它们表示为单独的属性已不再可行。所以我们重构了Company

public class Company : Entity  {
    public Address[] AddressBook { get; set; }
}

我们现在遇到了如何更新公司Address的问题。我们现在关心地址的身份,但仅在公司的背景下

重构后,我们最终得到:

public class Company : Entity  {
    public Address[] AddressBook { get; set; }

    public void UpdateAddress(Address newAddress) {
        var oldAddress = AddressBook.FirstOrDefault(a => a.Id == newAddress.Id);
        if (oldAddress != null)
            oldAddress = newAddress;
    }
}

public class Address : ValueObject {
    public Guid Id { get; private set; }
    public string ZipCode { get; private set; } // etc. 

    public Address(Guid id, string zipCode) {
        ZipCode = zipCode;
        Id = id;
    }
}

// usage
var company = repo.Load<Company>(companyId);
var address = new Address(model.Id, "12345"); // uses id of address we are replacing

company.UpdateAddress(address);
repo.Save(company);

要更新地址,我们会使用Id找到该地址,并将其替换为带有相同 ID的新地址对象。

  • 因此,Address仍然是一个值对象?
  • 我是否有权使用相同的 ID作为更新后的地址,或者按照定义(作为价值对象)我们替换 ID。
  • 如果地址仅在公司实体的上下文中具有标识,我们是否应该在我们的模型中明确说明,或许生成在实体内部唯一的Id(可能使用实体的id作为计算的一部分)?

1 个答案:

答案 0 :(得分:3)

  

因此,Address仍然是一个值对象吗?

我会说不,只要你给地址一个身份,它就不再是一个价值对象了。这意味着您只需停止关注其属性并开始重视应用程序中的地址生命周期,跟踪其状态的变化等。

  

我是否可以使用相同的Id来更新地址,或者使用   我们应该替换Id。作为一个值对象。

我不会重复使用地址ID。从域名的角度来看,当公司移动时,不是本身发生变化的地址。该地址仍然有一个有效的街道号码和建筑物。该身份定义的地址仍然存在,您甚至可以想象将其重新用于其他公司。因此,当公司移动时,只有公司和地址之间的关联需要更改为指向具有新ID的新地址。

  

如果地址仅在公司的上下文中具有身份   实体,我们是否应该在我们的模型中明确表示,或许可以生成   在实体内部唯一的Id(可能使用   实体的id作为计算的一部分)?

仅仅因为你觉得有必要在公司内部挑出不同的地址并不意味着你必须给他们一个Id并使他们成为实体而不是价值对象。例如,如果要对公司内的地址进行排名,请定义主要地址等,您可以使用值对象完美地执行此操作。地址值对象将保持不变,您可以在公司中拥有一个索引集合,一个PrimaryAddress字段,依此类推。