Two entities with identical fields are different things; two value objects with identical fields are the same value.
// value object: equality is by value, no id, immutable
final class Money
{
public function equals(self $other): bool
{
return $this->cents === $other->cents
&& $this->currency === $other->currency;
}
}
// entity: equality is by identity, and the fields change
final class Order
{
public function equals(self $other): bool
{
return $this->id->equals($other->id);
}
}
The practical consequence is that value objects need no table, no repository and no identity map, which removes a great deal of machinery from the parts of a model that are really just typed data. The test is whether replacing an instance with an equal one changes anything: swapping one £49.00 for another does not, and swapping one customer for another with the same name certainly does. Getting this wrong in the direction of entities is the more common and more expensive mistake.