5

I am reading Eric Evans book about DDD and I have a question to the following quote. How do you make your equals() method when you should not use the attributes? I am using JPA and I have a id attribute which is unique but this is not set until you actually persist the entity. So what do you do? I have implemented the equals method based on the attributes and I understand why you shouldn't because it failed in my project.

Section about entities:

When an object is distinguished by its identity, rather than its attributes, make this primary to its definition in the model. Keep the class definition simple and focused on life cycle continuity and identity. Define a means of distinguishing each object regardless of its form or history. Be alert to requirements that call for matching objects by attributes. Define an operation that is guaranteed to produce a unique result for each object, possibly by attaching a symbol that is guaranteed unique. This means of identification may come from the outside, or it may be an arbitrary identifier created by and for the system, but it must correspond to the identity distinctions in the model. The model must define what it means to be the same thing.

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

LuckyLuke
  • 47,771
  • 85
  • 270
  • 434
  • Possible duplicate, see http://stackoverflow.com/questions/5031614/the-jpa-hashcode-equals-dilemma – MRalwasser Feb 24 '12 at 13:27
  • The id works fine for me : http://stackoverflow.com/questions/7579404/using-auto-generated-id-of-hibenate-entity-object-in-the-equals-and-hashcode-met – NimChimpsky Feb 24 '12 at 14:54

4 Answers4

3

Couple approaches possible:

  • Use a business key. This is the most 'DDD compliant' approach. Look closely at domain and business requirements. How does your business identify Customers for example? Do they use Social Security Number or phone number? How would your business solve this problem if it was paper-based (no computers)? If there is no natural business key, create surrogate. Choose the business key that is final and use it in equals(). There is a section in DDD book dedicated to this specific problem.

  • For the cases when there is no natural business key you can generate UUID. This would also have an advantage in distributed system in which case you don't need to rely on centralized (and potentially unavailable) resource like database to generate a new id.

  • There is also an option to just rely on default equals() for entity classes. It would compare two memory locations and it is enough in most cases because Unit Of Work (Hibernate Session) holds on to all the entities (this ORM pattern is called Identity Map). This is not reliable because it will break if you use entities that are not limited to the scope of one Hibernate Session (think threads, detached entities etc)

Interestingly enough, 'official' DDD sample uses a very lightweight framework where every entity class is derived from Entity interface with one method:

boolean sameIdentityAs(T other) 
// Entities compare by identity, not by attributes.
Dmitry
  • 17,078
  • 2
  • 44
  • 70
  • Let say the entity was Question. What would I use? I have relied on the generated id until now. – LuckyLuke Feb 24 '12 at 13:31
  • Depends on your domain, maybe text of the question itself (in which case it maybe Value not Entity), but more likely something like question number. Really depends on your domain. – Dmitry Feb 24 '12 at 14:04
1

If the object is not persistent yet, then is there any harm in comparing 2 objects based on their attributes?

I am not sure why this failed in your project, but in my experience, comparison based on attributes almost always is slippery slope if your attributes are not final. That means, 2 objects that are equal now, may not be equal after sometime. This is very bad.

Given that most Java classes are written along with their accessors, equals comparing attributes are said to be a bad idea.

However, I would probably first check to see if the ID field is not null. If it is null, I would fall back to attribute comparison. If it is not null, then just use it and not do anything else. Does this make sense?

Pavan
  • 1,245
  • 7
  • 10
  • 2
    This is a dangerous thing to do. If you store the object in a HashSet before assigning its ID, the HashSet will be corrupted. – JB Nizet Feb 25 '12 at 08:14
  • For sure. But, then, I would assume you would be persisting the object in order to get the id. In which case, I generally prefer getting the persisted object which is returned and use that. Basically, do not mutate the object's state that is used in equality check. – Pavan Feb 25 '12 at 11:15
  • If you compare objects based on its attributes then it is behaving as a Value Object not an Entity. Value Objects and Entities have quite different semantics in DDD so you should not base Entity equality on attributes. – Declan Whelan Mar 11 '14 at 01:43
1

Given Person class with attributes name, surname. When Person at the age of 21 changes its name is it still the same Person (equals gives true)? If you write equals basis on attributes, then, it would not be the same person, so in my opinion the best approach is to test equality of entities basis on their business identifier (unique and immutable over the whole entity lifecycle).

omnomnom
  • 8,911
  • 4
  • 41
  • 50
0

Another solution could be to use an UUID field in your entity.

In this case, you could use the UUID as primary key or just for equals.

@Entity
public class YourEntity{ 

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

    // getter only...

}
vdenotaris
  • 13,297
  • 26
  • 81
  • 132