← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Value Object

quality PHP 8.1+ Intermediate

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;
    }
}

Added 15 Mar 2026
Edited 25 Mar 2026
Views 35
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S
No pings yesterday
Amazonbot 9 Perplexity 6 ChatGPT 3 Unknown AI 3 Ahrefs 3 Google 2 Qwen 1
crawler 25 crawler_json 2
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

✓ schema.org compliant