【问题标题】:DDD, value objects and ORMDDD、值对象和 ORM
【发布时间】:2010-10-31 06:19:00
【问题描述】:

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

如何欺骗 ORM?

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

提前致谢。

【问题讨论】:

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


    【解决方案1】:

    一个 VO 属于一个实体。 我们将使用Entity's ID(business ID)来追踪VO。

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

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

    【讨论】:

      【解决方案2】:

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

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

      以 JSON 表示形式存储值对象

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

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

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

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

      【讨论】:

        【解决方案3】:

        你有两个选择:

        • 值对象保存在同一个聚合根表中
        • 使用 聚合根 作为 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】:

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

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

          查看我的帖子here

          【讨论】:

          • 但我想表明代码中缺乏概念标识。顺便说一句,我以前看过那个帖子。
          • 澄清您的要求:您想隐藏 VO 对象上的 ID 属性,但需要 ORM 才能看到 ID 属性?两个问题:1)您的 ORM 可以访问私有/内部字段吗? (如 NHibernate)2)通过“隐藏”VO 的 ID 属性可以获得多少好处?
          • 忘了提-我正在使用NHibernate...那么-如何使用NHibernate?
          • 作为一个想法,您可以尝试使用显式接口实现(请参阅blog.briandicroce.com/2007/11/13/…)。创建一个带有 ID 属性的接口,然后在实体类上显式实现 ID 属性。我自己没有尝试过 - 如果它有效,请告诉我!
          【解决方案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 的实体。

          【讨论】:

          • 但是那么域模型只是1:1的数据库,地址仍然可以是一个值对象,仍然有一个单独的表。
          • 不,不是1:1的数据库。您有一个 Customer 类和一个 Adress 类(它是值对象)。在 NHibernate 中,一个值对象被映射为一个组件。只要您有一个需要 Id 的实体,它就不再是一个值对象。
          • 但是如果您需要将地址保存在表中,则需要数据库的 id。仅仅因为数据库需要一个 id 并不意味着该对象立即是一个实体。
          • 希望这个问题可以提供更多信息:stackoverflow.com/questions/679005/…
          • 在这种情况下 - 如何“隐藏” VO 的 id。我m not looking for way to completely get rid of it, cause then i wouldnt能够将VO数据存储在另一个表中。
          【解决方案6】:

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

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

          【讨论】:

          • 我认为这根本没有问题。埃里克·埃文(Eric Evan)让每个人都头晕目眩,因为不举例子就很容易谈论概念。
          • 值对象中的 id 字段则根据 DDD 并不能唯一标识您的值对象。它只是和其他属性值一样的另一个属性值。
          猜你喜欢
          • 2020-06-17
          • 1970-01-01
          • 1970-01-01
          • 2012-08-24
          • 2014-06-03
          • 2010-10-04
          • 2022-01-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多