A value is something intangible and immutable that measures, quantifies or describes a thing.
IN TRANSIT (for the status of a parcel in the logistics domain). You can easily infer the meaning of those values because their syntax are widely used and known. When exploring a domain while talking with an expert you can also found a lot of those values. I use to say that a typical domain would have 3/4 of values vs 1/4 of entities.
10 EURis an instance of an
2 Kgis a
54 mis a
IN TRANSITis a value of a
Parcel Delivery State.
A value is useful in a use-case
Characteristics of a Value
Immutable by definition: after the value is created, no mutation can change it (it's not a variable, operations on value produce new value in return).
Conceptual Whole: Only together do all the attributes form the complete intended measure or description.
E.g.: 10 €, 20 ℃, 50 ㎏. A value is completely defined by its attributes.
No identity: Just stating the value with all of its attributes is sufficient to grasp its meaning.
Equality: If all the attributes of two values are equals then the two values are considered equals. It's a smell to have equality defined by only a subset of attributes.
Intrinsic or natural ordering: Ordering may be based on magnitude, lexicographical or ordinal criteria. Be careful about imposing an ordering on unordered types. Ordering is not an essential feature of value type.
Constraints: Value types reflect constraints.
E.g. Amount quantity of currency is only positive (no negative amount can exist).
A phone number has min and max digits.
An ISBN has 13 digits (with the last digit being a check digit).
Don't change the value!
Return new value:
E.g.: 100 m + 50 m -> 150 m,
E.g.: 10 € + 20 € = 30 €
Can derive new value:
E.g.: converting speed from m/s to km/h (10 m/s -> 36 km/h)
E.g.: converting Euros in Dollars given a conversion rate. The Rate is itself a value, composed of two currencies (from, to), an interval of validity (a value with start and end instants) and a ratio (numerator and denominator).
Can extract details:
E.g.: extract the country dialing-code prefix from a phone number
Can format the value and return a string
Can construct a value and verify its constraints: constraints MUST be enforced at the value creation, not afterwards. The value constructor can be deemed as transactional: the value constraints are respected and the creation can occurs, or they are not met and the value DOESN'T EXISTS AT ALL!
System of Values
Value Type should be built with composition of other value types. The operations, relationships and constraints of values form a system of values.
|A positive number and the Meter unit
|Three positive numbers between 0 and 360 (for Degree, Minutes and Seconds) if degree of arc unit is used
|48° 51' 23.81"
|Distance from center, Latitude ArcDegree, Longitude ArcDegree
|(6 371 km, 48° 51' 23.81" North, 2 21'7.99 East")
|A positive number and the Second unit
|A Distance per Duration
|A modification of speed per second (aka. derivative)
Value Type design tips
Construction should always result in a meaningful and correct value: Partial initialisation is always problematic. The value constructor must be transactional: it returns a correct value or throws an exception.
Respect and enforce state invariant A value
Comparison and Equality is a fundamental concept of a value and an
equalsoperation should always be provided. Total ordering may apply or not depending on the concept (E.g. an amount can be compared and sorted but not a phone number)
Value Type Design Smells
Anemic Value Type: no or very few behavior beyond attributes access
Role Creep: too much responsibilities and external dependencies
Non-meaningul constructor: constructor without useful enforcement of constraints or sufficient quality of failure
Memory Representation and Serialization
value-of: this function takes an input string and returns the in-memory representation of the value.
to-string: this function takes an in-memory representation and returns a string. Of course
(= v (value-of (to-string v)))and
(= s (to-string (value-of s)))as
to-stringare inverse functions (operations that undo each other).
Equality and Hashcode
hashcode are two additional useful operations.
equals: must operates on all the attributes of the value
hashcode: should be coherent with the value, equal value should have identical hashcode
compare function with two arguments (x and y), returns a negative number, zero, or a positive number when x is logically 'less than', 'equal to', or 'greater than' y.
Value Object in an Object-Oriented Language
Transactional Constructor: the value exists and is correct or it doesn't exist at all, there is nothing in between.
Very strict encapsulation of the attributes: no mutation of any attributes is allowed to keep the value immutable.
No mutation methods: the methods exposing behavior returns a new value and never change the existing attributes.
Brings the language of the domain into the code: Value types add the domain terminology into the codebase.
is a Complexity Swallower: Compound value types swallow lots of computational complexity
Progressive introduction: Value types can be added progressively into a codebase. E.g. even if some part of the code continue to use BigDecimal to represents amount, you can introduce
is Testable: value type being immutable and with low coupling, they are very easy to unit tests.
is Concurrency-safe: value type being immutable, the concerns of the same code being accessed simultaneously by multiple threads disappear.
Relieve entities of complexity: especially all the surface control rules ("does this value respects these constraints?")
Composes easily and improves extensibility
Value Types is the DDD building block with the best Return-On-Investment! (ROI).