Value Object
Also Known As
value object pattern
VO
immutable value
TL;DR
A small immutable object defined by its value rather than its identity — two Value Objects with the same data are equal.
Explanation
Value Objects (Domain-Driven Design) encapsulate primitive values with business meaning and enforce constraints at construction. A Money(amount: 100, currency: 'GBP') value object guarantees valid currency, prevents negative amounts, and makes equality meaningful. They are immutable (operations return new instances), have no identity (two identical Value Objects are the same thing), and are self-validating. They replace primitive obsession, make invalid state unrepresentable, and greatly improve domain model expressiveness.
Common Misconception
✗ Value objects are just DTOs with a fancier name. DTOs carry data between layers with no behaviour. Value objects encapsulate a concept, enforce their own invariants on construction, and are compared by value not identity — they can have rich domain behaviour.
Why It Matters
Value objects are defined by their attributes, not identity — two Money(100, 'USD') instances are equal because they represent the same value, enabling safe equality comparisons and immutable domain modelling.
Common Mistakes
- Mutable value objects — a Money object whose amount can be changed after construction breaks equality semantics.
- Not overriding equality methods — PHP's == compares object properties by default but === requires same instance.
- Value objects that hold references to entities — they become contextually dependent and lose their value semantics.
- Using value objects for things that have identity — a User is an entity, not a value object, even if two users have the same name.
Avoid When
- The concept has identity that matters — two users with the same name are not the same user; use an entity instead.
- The object is mutable — value objects must be immutable; mutable value objects cause subtle aliasing bugs.
- Wrapping every primitive in a value object — over-engineering that adds noise without meaningful domain modelling.
When To Use
- Domain concepts defined entirely by their attributes — Money, EmailAddress, Coordinates, DateRange.
- Enforcing invariants at construction time — an EmailAddress object is always valid; a plain string is not.
- Replacing primitive obsession — replacing string $email with EmailAddress $email makes intent clear.
- Immutable data that benefits from equality-by-value semantics rather than identity.
Code Examples
✗ Vulnerable
// Primitive obsession — no validation, no behaviour
function transfer(int $amount, string $currency): void {}
✓ Fixed
readonly class Money {
public function __construct(
public int $amount, // in minor units (pence/cents)
public string $currency, // ISO 4217
) {
if ($amount < 0) throw new \InvalidArgumentException('Amount cannot be negative');
if (strlen($currency) !== 3) throw new \InvalidArgumentException('Invalid currency code');
}
public function add(self $other): self {
if ($this->currency !== $other->currency) throw new CurrencyMismatch();
return new self($this->amount + $other->amount, $this->currency);
}
public function equals(self $other): bool {
return $this->amount === $other->amount && $this->currency === $other->currency;
}
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
25 Mar 2026
Views
35
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 1
No pings yesterday
Amazonbot 9
Perplexity 6
ChatGPT 3
Unknown AI 3
Ahrefs 3
Google 2
Qwen 1
Also referenced
How they use it
crawler 25
crawler_json 2
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: Medium
⚡ Quick Fix
Create an immutable class for domain concepts like Email, Money, or UserId — validate in the constructor, use readonly properties, and compare by value not identity
📦 Applies To
PHP 8.1+
web
cli
queue-worker
🔗 Prerequisites
🔍 Detection Hints
Email address as raw string passed through 5 functions with repeated validation; Money as float causing rounding errors
Auto-detectable:
✗ No
phpstan
psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Class
Tests: Update