DDD,值对象和ORM

时间:2009-06-04 08:45:55

标签: domain-driven-design identity value-objects

值对象没有标识。 ORM需要标识来更新数据库。

如何欺骗ORM?

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

提前致谢。

6 个答案:

答案 0 :(得分:43)

当Eric Evans谈到“实体有身份,Value Objects没有”时,他不是在谈论数据库中的ID列 - 他在谈论身份作为概念

VO没有概念身份。这并不意味着他们不应该拥有持久性身份。不要让持久性实现让您了解实体与VO。

请参阅我的帖子here

答案 1 :(得分: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; }
}

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

CREATE TABLE Customers
(
    CustomerID,

    LastName,

    HomeAddress_Street,

    HomeAddress_City,

    HomeAddress_ZipCode,
)

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

答案 2 :(得分:4)

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

它可能不是真正的DDD,但它适用于我。

答案 3 :(得分: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方法

答案 4 :(得分:0)

先前答案中提到的用于持久保存值对象的所有选项-例如将值对象属性展平为它们所属的实体表的列,或者通过为数据模型包括唯一ID将其持久化在单独的表中-有效并得到充分解释。当然,这些选项通常独立于特定的基础数据库技术而适用,这是一个很好的选择。

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

以JSON表示形式存储值对象

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

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

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

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

答案 5 :(得分:0)

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

VO也可能包含其他实体/ VO,它仅表示OO,封装。 以E-R,1:N为例,我们可以使用联合表对其进行持久化。

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