What's a Value?

Identifying Value in a domain is a fundamental practice of Domain-Driven Design, this article gives you concrete tips for identifying and building your Value Types
·
DDD's Value Type concept has tremendous benefits, firstly by introducing the domain into the code but moreover by absorbing lots of domain complexity and simplifying the implementation with its immutability feature. This article explores the Value type characteristics and how we can use it when modeling a domain.

Definition

A value describes a thing and is meaningful by itself.
This is the point of a value: just by stating a value your interlocutor understands its meaning. If I say to you "10 Euros" or "50 meters" you understand their meanings (given you know what a currency or a distance is of course). A value can be understood as a whole.
A value is something intangible and immutable that measures, quantifies or describes a thing.
Intangible and immutable means the value has the same meaning whatever the location and time. That's to say we remove time and space from the landscape then greatly reducing complexity by NOT dealing with state lifecycle and identity (for the value, see Entity that will deal with state).
E.g.: 10 EUR, 2 Kg, 54 m, 2022/08/14 or 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.
A value is of course an instance of a value type, using the Meta prefix, the Value Type is on a Meta level above the value:
  • 10 EUR is an instance of an Amount,

  • 2 Kg is a Weight,

  • 54 m is a Distance,

  • 2022/08/14 is a Date using a Gregorian Calendar,

  • IN TRANSIT is a value of a Parcel Delivery State.

The concept of "Value" comes from the Functional Programming world. Data modeling uses a concept quite close it's quite different (see our article Data Model vs Domain Model). How to recognize a value type when encountering a domain concept?

A value is useful in a use-case

Here is a value:
That's a 10 Euros Bank Note, everyone knows what it is and what it can be used for. Actually only the "10 Euros" value is interesting for the consumer in a transaction with a merchant. The Bank note has a face value but also a serial number that identify each bank note. But this serial number is not useful in the context of the commercial transaction, only in the context of a central bank to know how many bank notes are in the market.
It's useful to look at Entity to better understand what a value is:
Is it a value or an entity? well it depends of the usage, if you're not interested in the lifecycle of the concept then from your point of view it's a value and you can consider this concept as read-only. Like for this bank note example, as a consumer using it to buy something, the identity of a particular bank note doesn't matter. That's

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).

Value Operations

Value is represented using data, but a value also has behaviors. A value is NOT only a data structure, a value has operations (like an Abstract Data Type). A value doesn’t change, an operation executed on a value returns a new value.
Operation on a value:
  • 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.

Example N°1

AttributesValue TypeExample
A positive number and the Meter unitDistance100 meters
Three positive numbers between 0 and 360 (for Degree, Minutes and Seconds) if degree of arc unit is usedArc48° 51' 23.81"
Distance from center, Latitude ArcDegree, Longitude ArcDegreePositionOnSphere(6 371 km, 48° 51' 23.81" North, 2 21'7.99 East")
Diagram of Position on a Sphere
Position on sphere could have been implemented directly using 6 numbers for the latitude and longitude but the logic and constraints regarding Arc would have been embedded inside the value. A better approach is to decompose the problem and rely on more unitary value that embed their logic, here: a Distance and two Arc values representing longitude and latitude that compose to form a PositionOnSphere value.

Example N°2

AttributesValue TypeExample
A positive number and the Second unitDuration32 seconds
A Distance per DurationSpeed10 m/s
A modification of speed per second (aka. derivative)Acceleration3 m/s²
Acceleration
Again, the Acceleration value is composed of speed variation per duration , here 1 second. Speed itself is composed of distance per duration. Identifying value types tremendously helps to swallow and divide complexity into manageable chunks.

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 equals operation 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

Here are some smells for a Value Type:
  • 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

Value construction

The value construction method or function should check all the constraints on attributes and throws an exception whenever one of this constraint is violated. That's to say in invalid value can't exists. A partial construction (some attributes but not all) should not occurs, that's the problem with the builder pattern that is readable but can lead to impartial creation. The construction is said to be transactional. The constructor arguments should be the unitary value of each of its attributes, a one-arity argument with a map can ease the construction process.

Memory Representation and Serialization

A value has an optimized memory representation which is an implementation detail that must be hidden. A value is often conveyed through a string representation and transforming from string to the in-memory value is vice-versa are value's operations always present. These transforming operations are to-string and value-of.
  • 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 value-of and to-string are inverse functions (operations that undo each other).

Equality and Hashcode

equals and 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

Optional Comparison

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

A Value Object is a specific way of implementing a Value - something immutable - in an object oriented language, where everything is mutable by design. This is done in the following way:
  • 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.

Actually, a value object can be considered as an oxymoron as a value can't change and an object is by defintion something that change.

Benefits

Value type:
  • 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).

Value and Entity

Values capture the state of entity.