【问题标题】:Entities in domain driven design领域驱动设计中的实体
【发布时间】:2012-02-24 09:35:50
【问题描述】:

我正在阅读 Eric Evans 关于 DDD 的书,我对以下引用有疑问。当你不应该使用属性时,你如何制作你的 equals() 方法?我正在使用 JPA,并且我有一个唯一的 id 属性,但是在您实际持久化实体之前不会设置它。所以你会怎么做?我已经根据属性实现了 equals 方法,我理解为什么不应该这样做,因为它在我的项目中失败了。

关于实体的部分:

当一个对象以它的身份而不是它的身份来区分时 属性,使其成为模型中定义的主要内容。保持 类定义简单且专注于生命周期的连续性和 身份。定义区分每个对象的方法,无论 它的形式或历史。警惕需要匹配的需求 对象按属性。定义一个保证 为每个对象产生一个独特的结果,可能通过附加一个 保证唯一的符号。这种识别方式可以 来自外部,或者它可能是由创建的任意标识符 和系统,但它必须对应于身份 模型中的区别。模型必须定义它的意义 同样的事情。

http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

【问题讨论】:

标签: java jakarta-ee domain-driven-design


【解决方案1】:

可能的几种方法:

  • 使用业务密钥。这是最“符合 DDD”的方法。密切关注领域和业务需求。例如,您的企业如何识别客户?他们使用社会安全号码或电话号码吗?如果是纸质的(没有计算机),您的企业将如何解决这个问题?如果没有自然业务密钥,则创建代理。选择最终的业务密钥并在equals() 中使用。 DDD 书中有一节专门讨论这个特定问题。

  • 对于没有自然业务密钥的情况,您可以生成 UUID。这在分布式系统中也有优势,在这种情况下,您无需依赖数据库等集中式(并且可能不可用)资源来生成新 id。

  • 还有一个选项只依赖默认的equals() 实体类。它会比较两个内存位置,在大多数情况下就足够了,因为Unit Of Work(休眠会话)保留了所有实体(这个 ORM 模式称为Identity Map)。这是不可靠的,因为如果您使用不限于一个 Hibernate 会话范围的实体(想想线程、分离的实体等),它会中断

有趣的是,“官方”DDD 示例使用了一个非常轻量级的框架,其中每个实体类都通过一个方法从Entity 接口派生:

boolean sameIdentityAs(T other) 
// Entities compare by identity, not by attributes.

【讨论】:

  • 假设实体是问题。我会用什么?到目前为止,我一直依赖生成的 id。
  • 取决于您的域,可能是问题本身的文本(在这种情况下可能是值而不是实体),但更可能是问题编号。真的取决于您的域。
【解决方案2】:

如果对象还没有持久化,那么根据属性比较两个对象有什么害处吗?

我不确定为什么这会在您的项目中失败,但根据我的经验,如果您的属性不是最终的,则基于属性的比较几乎总是很容易出错。这意味着,现在相等的 2 个对象在某个时间后可能不相等。这真是太糟了。

鉴于大多数 Java 类是连同它们的访问器一起编写的,所以用 equals 比较属性被认为是一个坏主意。

但是,我可能会首先检查 ID 字段是否不为空。如果它为空,我会回退到属性比较。如果它不为空,那么只需使用它,不要做任何其他事情。这有意义吗?

【讨论】:

  • 这是一件危险的事情。如果在分配 ID 之前将对象存储在 HashSet 中,则 HashSet 将损坏。
  • 当然。但是,那么,我会假设您将持久化对象以获取 id。在这种情况下,我通常更喜欢获取返回的持久对象并使用它。基本上,不要改变用于相等检查的对象状态。
  • 如果您根据对象的属性比较对象,那么它的行为是值对象而不是实体。值对象和实体在 DDD 中具有完全不同的语义,因此您不应将实体相等性基于属性。
【解决方案3】:

给定具有namesurname 属性的Person 类。当 21 岁的人更改其名称时,它仍然是同一个人(等于true)吗? 如果你在属性上写相等的基础,那么它不会是同一个人,所以我认为最好的方法是根据业务标识符(在整个实体生命周期中唯一且不可变)测试实体的相等性。

【讨论】:

    【解决方案4】:

    另一种解决方案是在您的实体中使用 UUID 字段。

    在这种情况下,您可以使用 UUID 作为主键或仅用于等号。

    @Entity
    public class YourEntity{ 
    
        @Id
        private String uuid = UUID.randomUUID().toString();
    
        // getter only...
    
    }
    

    【讨论】:

      猜你喜欢
      • 2020-10-26
      • 2011-08-01
      • 2015-04-22
      • 1970-01-01
      • 2011-01-30
      • 2011-01-05
      • 2010-10-05
      • 2013-12-07
      • 1970-01-01
      相关资源
      最近更新 更多