Skip to main content

Value Object

Value Object models a concept by its attributes rather than identity, is immutable, and is compared by value. It eliminates primitive obsession, centralizes validation, and yields safe, expressive, thread-safe domain types.

Type
Data
When to Use
Identity Irrelevant, Immutable Domain Concept, Replace Primitive Obsession

Value Object is a domain-modeling pattern for objects whose identity is irrelevant: what matters is the combination of their attributes, not which instance they are. Two value objects with the same attribute values are considered equal and interchangeable. Money, a date range, a geographic coordinate, and an email address are typical examples. The pattern is a foundational building block of domain-driven design.

How It Works

A value object holds attributes and is defined to be immutable: once created, its state never changes, and any operation that would modify it returns a new instance instead. Equality is based on structural comparison of all attributes rather than reference identity, so two Money(10, "USD") instances are equal. Validation lives in the constructor, so a value object cannot exist in an invalid state, for example an Email that is not well-formed. Because they are immutable and side-effect free, value objects are inherently thread-safe and freely shareable.

Behavior relevant to the concept belongs on the value object itself, such as money.add(other) or dateRange.overlaps(other), which keeps related logic cohesive and out of services.

When to Use It

Use Value Objects when a concept is defined wholly by its values and has no meaningful identity, when you want to eliminate primitive obsession by replacing bare strings and numbers with meaningful types, or when you need guaranteed-valid, immutable, comparable domain concepts. They make models more expressive and push validation to a single place.

Trade-offs

Creating many small types adds classes and some boilerplate, though records and data classes in modern languages reduce this sharply. Persisting value objects can be awkward, since they often map to several columns or an embedded structure rather than a table with an identifier. Immutability means producing new instances on every change, which is usually negligible but can matter in tight loops with very large objects. Equality and hashing must be implemented correctly to avoid subtle bugs in collections.

Related Patterns

Value Objects are an alternative to Entity, which is defined by identity that persists through attribute changes. They are commonly embedded in aggregates persisted via Repository and Data Mapper. Domain Event objects are frequently modeled as immutable value objects describing something that happened.

Example

Replacing a raw amount: number, currency: string pair with a Money value object centralizes currency rules: the constructor rejects negative amounts, add refuses to combine mismatched currencies, and two equal amounts compare as equal. The surrounding code becomes safer and clearer, and invalid money simply cannot be constructed.