Entities and Values

In object-oriented programming, defining new classes of objects is the typical way of introducing new types. It is extremely important to classify these types in order to ensure separation of concerns. In this article, I am going to define two common classes of types, namely entities and values.

Entities are objects of the domain model, most likely evolving over time. They encapsulate attributes that can themselves be entities. For example, a Car might encapsulate four Wheels and an Engine.

Entities are uniquely identified by an id. This identifer can be the actual memory location of the entity in some simple use cases, but a more involved identification scheme is usually required. If the entity is persisted in a relational database for instance, its unique identifier is usually the table’s primary key. It is a good practice to introduce a type safe wrapper around the entity’s identifier.

GOOD PRACTICE

private Id<Car> id;

Equality is defined uniquely by the entity’s identifier.

GOOD PRACTICE

class Car {

  ...

  @Override
  public boolean equals(Object obj) {
    ...
    return id.equals(that.id);
  }

}

It is a very bad idea to define equality based on the entity’s attributes, as they might change over time even though the entity itself doesn’t change. Defining equality using a subset of the entity’s attributes is an equally bad idea, unless you can justify that the subset is actually the entity’s unique identifier.

BAD PRACTICE

class Car {

  ...

  @Override
  public boolean equals(Object obj) {
    ...
    return licensePlate.equals(that.licensePlate);
  }

}

An entity should never instantiate other entities directly. All dependencies must be passed either through the constructor or through additional setters. It is also important that an entity can be constructed by passing nulls to facilitate partial domain model instantiations in unit tests.

BAD PRACTICE

Car(Wheel[] wheels, Engine engine) {
  this.wheels = checkNotNull(wheels);
  this.engine = checkNotNull(engine);
}

Entities are marshalled into complex structures. For instance, one would map an entity to a relational database table or a JSON object.

@Entity
class Car {

  @Value
  private Wheel[] wheels;

  @Value
  private Engine engine;

}

Values are immutable objects describing a type of data. For instance, EmailAddresses and Symbols are values. A value usually restricts the universe of its underlying type. For instance, an EmailAddress restricts the universe of Strings to the format defined in RFC 5322.

GOOD PRACTICE

class EmailAddress {
  
  private final String emailAddress;

  EmailAddress(String emailAddress) {
    checkNotNull(emailAddress);
    ...
    this.emailAddress = emailAddress;
  }

}

Equality is defined based on the value semantics. The EquivalenceTester in kaChing’s kawala is especially useful to test it.

EquivalenceTester.check(
    newArrayList(
        new Symbol("GOOG"),
        new Symbol("goog")),
    newArrayList(
        new Symbol("AAPL")));

Values are marshalled into simple structures, usually by relying on its underlying type. For instance, an EmailAddress can be marshalled as a varchar(255) column or a JSON string.

@Entity
class Customer {

  @Value(type = EmailAddressType.class)
  private EmailAddress emailAddress;

}