First, Value objects - like most ddd implements - should be modeled to address some part of the ubiquitous language of your domain.
When to create a Value Object? In the book Implementing Domain Driven Design
, Vaughn Vernon gives these characteristics of a Value Object
- It measures, quantifies, or describes a thing in the domain.
- It
can be maintained as immutable.
- It models a conceptual whole by
composing related attributes as an integral unit.
- It is completely
replaceable when the measurement or description changes.
- It can be
compared with others using Value equality.
- It supplies its
collaborators with Side-Effect-Free Behavior
The first bulletin means that a VO should measure, quantify, or describe a thing in the domain.
Next, it should be immutable, and should not have state changing methods. That also goes for any objects inside the VO, they too must never be able to change if the VO uses them.
Moving on, a VO must be complete after construction. That means all of its attributes and properties that it needs must be in a valid state. Example: A price VO must set both currency
and amount
properties with correct values in the constructor.
Replicability. This means that a VO can be replaced with another VO of the same type. If your VO can set currency
to Yen as opposed to Dollar then this might become a problem when later trying to replace the the VO because you want to change(replace) the amount
for price
.
Example: an Entity accepts the VO Price
which has a currency
of Dollar and an amount
of 100,000. If we could construct a new VO of the same type with currency
of Yen and an amount
of 200,000 then this VO might not be replaceable. This will depend on your domain of course, but if the Entity's intent is to simply replace the monetary value of the VO only, then it might be better to have separate DollarVO
and YenVO
to avoid creating a VO with an impedance mismatch of properties.
Comparison: VO's should be comparable to other VO instances of the same type. If a VO can be constructed with the currency
property but in other cases it is not, then this would potentially violate comparability. IE. you should be able to take two instances of the same VO and compare their properties checking for different values of those properties only. If a property is missing then the comparison fails for the wrong reason.
The last principle is parcel to Immutability. If you're gonna give VO's methods, then make sure they ain't changing nothing.