Entities
An entity is used to represent a domain concept distinguished by an identity.
This identity must remain the same through the whole entity lifecycle.
Characteristics
Entities represent a thread of continuity and identity, going through a lifecycle, though their attributes may change. They are not defined primarily by their attributes but by their identity that stays the same through time and across distinct representations.
Identity
The identity of an entity must be unique and immutable. It must be chosen carefully and well defined in the model. Identification can come from:
- The outside: a user of the system can provide the identity, handling the uniqueness himself.
- The inside: the entity can generate its own identity using an algorithm.
- An identity generator, like a database sequence.
It is often a good idea to use value objects as identifiers, particularly in the case of a composite identity. Its consistency and immutability can then be delegated to the value object.
Behavior
Entities should not be merely holders of attributes, but should also contain the behavior that is directly relevant to them. Do not create entities with only getters and setters but add methods with meaningful names, implementing domain behavior.
When the behavior does not fit naturally into a specific entity, for instance if multiple entities are involved, you move the behavior to domain services.
Declaration
To declare an entity with the business framework, you have two alternatives.
Extend the BaseEntity
class:
public class SomeEntity extends BaseEntity<SomeEntityId> {
@Identity
private SomeEntityId id;
public SomeEntity(SomeEntityId id) {
this.id = id;
}
// Other methods
}
By extending BaseEntity
, you will have a default implementation of the
equals()
and hashCode()
methods, consistent with the definition of an entity. A toString()
method is also provided
by default.
- If the identity is in a field named
id
, it will be automatically discovered. - Otherwise, you can mark the identity field with the
@Identity
annotation. - Alternatively you can override the
getId()
method to return the identity.
Implement the Entity
interface:
public class SomeEntity implements Entity<SomeEntityId> {
private SomeEntityId id;
public SomeEntity(SomeEntityId id) {
this.id = id;
}
public int hashCode() {
// TODO: implement using identity attribute only
}
public boolean equals() {
// TODO: implement using identity attribute only
}
@Override
public SomeEntityId getId() {
return this.id;
}
// Other methods
}
Implementing Entity
allows you to fully control the inheritance of your
entity. However, you will have to implement equals()
and hashCode()
methods yourself, consistently with the definition
of an entity (i.e. based on the identity only).
You must implement the getId()
method as the framework will often need to retrieve the entity identity.
Example
public class Customer extends BaseEntity<CustomerId> {
private final CustomerId id;
private String name;
private String email;
public Customer(CustomerId id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public void changeEmail(String newEmail) {
if (!isEmailValid(newEmail)) {
throw new CustomerException("Invalid email: " + newEmail);
}
email = newEmail;
}
public void changeName(String newName) {
if (newName.isEmpty()) {
throw new CustomerException("Name cannot be blank");
}
name = newName;
}
// other methods
}
Notice:
- How the identity is defined as an immutable value object and cannot be changed by external objects.
- How method names have meaningful names and implement domain behavior.