17

值对象没有身份。ORM 需要身份来更新数据库。

如何欺骗ORM?

(将值对象的 Id 标记为内部将不起作用,因为 ORM 存在于不同的程序集中,并且将其移动到同一程序集是不可接受的)。

提前致谢。

4

6 回答 6

51

当 Eric Evans 谈到“实体有身份,值对象没有”时,他不是在谈论数据库中的 ID 列——他是在谈论身份作为一个概念

VO 没有概念上的身份。这并不意味着他们不应该有持久性身份。不要让持久性实现影响您对实体与 VO 的理解。

在这里查看我的帖子。

于 2009-06-05T10:30:31.817 回答
5

就我对 DDD 的理解而言,值对象只是划分实体的一种方式。如果一个值对象应该与一个 ID 一起存储在数据库中,那么它就不是一个值对象。

例子:

域模型如下所示(C#):

public class Customer : Entity
{
    public Guid CustomerID { get; }

    public string LastName { get; set; }

    public Address HomeAddress { get; set; }
}

public class Address : ValueObject
{
    public string Street { get; set; }

    public string City { get; set; }

    public string ZipCode { get; set; }
}

相应的数据库表看起来像这样(伪 SQL):

CREATE TABLE Customers
(
    CustomerID,

    LastName,

    HomeAddress_Street,

    HomeAddress_City,

    HomeAddress_ZipCode,
)

要将地址存储在单独的表中,您可以使其成为具有 ID 的实体。

于 2009-06-05T08:26:41.910 回答
4

就我个人而言,我在值对象中有 Id 字段 - 我将其视为值对象的另一个属性(例如名称、位置等)。

它可能不是真正的 DDD,但它对我有用。

于 2009-06-05T08:06:06.977 回答
0

您有 2 个选项:

  • 将值对象保存在同一个聚合根表中
  • 使用聚合根作为 ID的单独表

对于您的示例,例如:

public class Customer : Entity
{
    public Guid CustomerID { get; }
    public string LastName { get; set; }
    public Address HomeAddress { get; set; }
}

public class Address : ValueObject
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

选项 1(伪 SQL):

​CREATE​ ​TABLE​ Customer (
      // aggregate root
​     customerId ​int​ ​NOT​ ​NULL​,
      lastName VARCHAR(30),

      // value object
      street VARCHAR(100),
      city VARCHAR(50),
      zip VARCHAR(10)
​     ​CONSTRAINT​ PK_Customer ​PRIMARY​ ​KEY​ (customerId)
​   )

选项 2(伪 SQL):

// aggregate root
CREATE​ ​TABLE​ Customer (
​   customerId ​int​ ​NOT​ ​NULL​,
    lastName VARCHAR(30)
    CONSTRAINT​ PK_Customer ​PRIMARY​ ​KEY​ (customerId)
    )

// value object
CREATE​ ​TABLE​ Address (     
​     customerId ​int​ ​NOT​ ​NULL​, // same ID from Customer

​     street VARCHAR(100),
      city VARCHAR(50),
      zip VARCHAR(10)
      ​CONSTRAINT​ PK_Address ​PRIMARY​ ​KEY​ (customerId)
    )
  • 然后你可以创建一个toDomain(sqlResult)函数来转换你的域对象中的查询结果
  • 尝试one-table默认使用方法
于 2020-07-27T18:05:42.050 回答
0

前面的答案中提到的用于持久化值对象的所有选项 - 例如将值对象属性扁平化为它们所属的实体表的列,或通过包含数据模型的唯一 ID 将它们持久化在单独的表中 - 都是有效且良好的解释。当然,这些选项通常独立于特定的底层数据库技术而适用,这是一个很大的优势。

但我认为至少值得一提一些其他选项,这些选项在许多情况下已经足够且易于实施:

以 JSON 表示形式存储值对象

当然,这取决于您的技术限制,但现在许多数据库以及 ORM 解决方案甚至提供了对 JSON 表示的内置支持。有些甚至包括搜索选项。如果您不期望有大量的项目,您甚至可以通过将此列表作为对象的 JSON 集合直接保存在实体表中来将此方法用于实体内的值对象列表。

当然,除了 JSON 之外,还支持其他格式(例如纯文本或 XML),但根据我的经验,我发现 JSON 是最舒服的。

使用基于文档的存储解决方案

值得一提的是,选择基于文档的数据库技术(例如MongoDB)还为持久化域模型实体提供了新选项,因为它允许将聚合作为包括其所有子实体和/或值的整个文档持久化对象。

于 2020-08-03T22:57:07.863 回答
0

一个 VO 属于一个实体。我们将使用实体的 ID(企业 ID)来跟踪 VO。

一个VO也可能包含其他实体/VO,它只是代表OO,封装。举个例子,ER,1:N,我们可以用Union Table来持久化它。

专注于业务,而不是那些概念。

于 2020-12-14T08:59:23.803 回答