领域驱动设计中的实体

5
我正在阅读Eric Evans关于DDD的书籍,对以下引用有疑问。当您不应使用属性时,如何制作equals()方法?我正在使用JPA,我有一个唯一的id属性,但是只有在实际持久化实体之后才设置该属性。那你怎么办?我已经根据属性实现了equals方法,并且我理解为什么不应该这样做,因为它在我的项目中失败了。
实体部分:
当一个对象因其身份而不是其属性而被区分时,在模型中将其定义为主要内容。保持类定义简单并专注于生命周期连续性和标识。定义一种区分每个对象的方法,而不考虑其形式或历史记录。警惕需要通过属性匹配对象的要求。定义一种操作,保证为每个对象产生唯一结果,可能是通过附加保证唯一的符号来完成的。这种识别方式可以来自外部,也可以是由系统创建的任意标识符,但它必须对应于模型中的身份差异。模型必须定义什么是相同的东西。

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


可能是重复问题,请参考 https://dev59.com/Fm445IYBdhLWcg3wGmo6 - MRalwasser
我的id正常工作: https://dev59.com/FlvUa4cB1Zd3GeqPwLU_ - NimChimpsky
4个回答

3

有几种可能的方法:

  • 使用业务键。这是最符合“DDD”的方法。仔细查看领域和业务要求。您的企业如何识别客户?他们使用社会安全号码还是电话号码?如果是基于纸张(没有计算机)的情况,您的企业将如何解决此问题?如果没有自然的业务键,则创建替代键。选择最终的业务键并在equals()中使用它。DDD书籍中有一节专门讲解这个问题。

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

  • 还有一个选项,就是只依赖于实体类的默认equals()。它会比较两个内存位置,在大多数情况下足够了,因为工作单元(Hibernate Session)保留了所有实体(这个ORM模式称为Identity Map)。这不是可靠的,因为如果您使用不限于一个Hibernate会话范围的实体(考虑线程,分离实体等),它将会出错。

有趣的是,“官方”的DDD样例使用非常轻量级的框架,其中每个实体类都派生自Entity接口,并包含一个方法:

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

假设实体是问题(Question),我应该使用什么?直到现在,我一直依赖于生成的ID。 - LuckyLuke
根据您的领域而定,可能是问题本身的文本(在这种情况下可能是值而不是实体),但更有可能是问题编号。这真的取决于您的领域。 - Dmitry

1

如果对象还没有持久化,那么基于它们的属性进行比较会有什么危害吗?

我不确定为什么在你的项目中会失败,但是根据我的经验,如果你的属性不是最终的,那么基于属性进行比较几乎总是一个棘手的问题。这意味着,现在相等的两个对象,在某个时候可能不再相等。这非常糟糕。

考虑到大多数Java类都是与它们的访问器一起编写的,因此使用equals比较属性被认为是一个坏主意。

然而,我可能会首先检查ID字段是否为空。如果它为空,我会退回到属性比较。如果它不为空,那么就直接使用它,不做其他操作。这有道理吗?


2
这是一件危险的事情。如果在分配对象ID之前将对象存储在HashSet中,那么HashSet将会被破坏。 - JB Nizet
当然。但是,我会假设您将持久化对象以获取ID。在这种情况下,我通常更喜欢获取返回的持久化对象并使用它。基本上,不要改变用于相等性检查的对象状态。 - Pavan
如果您根据对象的属性进行比较,那么它就表现为值对象而不是实体。在领域驱动设计中,值对象和实体具有完全不同的语义,因此您不应该基于属性来确定实体的相等性。 - Declan Whelan

1
给定一个具有属性名称和姓氏的“人”类。当一个人到了 21 岁时,如果更改其姓名,那么它还是同一人(equals 给出 true 吗)? 如果您根据属性编写 equals,则不会是同一个人,因此在我看来,最好的方法是基于业务标识符(在整个实体生命周期中唯一且不可变)测试实体的相等性。

0

另一种解决方案可能是在实体中使用一个UUID字段。

在这种情况下,您可以将UUID用作主键或仅用于相等比较。

@Entity
public class YourEntity{ 

    @Id
    private String uuid = UUID.randomUUID().toString();

    // getter only...

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接