{
    "slug": "value_object",
    "term": "Value Object",
    "category": "quality",
    "difficulty": "intermediate",
    "short": "A small immutable object defined by its value rather than its identity — two Value Objects with the same data are equal.",
    "long": "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.",
    "aliases": [
        "value object pattern",
        "VO",
        "immutable value"
    ],
    "tags": [
        "ddd",
        "oop",
        "design-pattern",
        "immutability"
    ],
    "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."
    ],
    "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."
    ],
    "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."
    ],
    "related": [
        "immutability",
        "primitive_obsession",
        "readonly_properties",
        "domain_driven_design"
    ],
    "prerequisites": [
        "immutability",
        "readonly_properties",
        "ddd_value_objects"
    ],
    "refs": [
        "https://martinfowler.com/bliki/ValueObject.html"
    ],
    "bad_code": "// Primitive obsession — no validation, no behaviour\nfunction transfer(int $amount, string $currency): void {}",
    "good_code": "readonly class Money {\n    public function __construct(\n        public int    $amount,   // in minor units (pence/cents)\n        public string $currency, // ISO 4217\n    ) {\n        if ($amount < 0)         throw new \\InvalidArgumentException('Amount cannot be negative');\n        if (strlen($currency) !== 3) throw new \\InvalidArgumentException('Invalid currency code');\n    }\n    public function add(self $other): self {\n        if ($this->currency !== $other->currency) throw new CurrencyMismatch();\n        return new self($this->amount + $other->amount, $this->currency);\n    }\n    public function equals(self $other): bool {\n        return $this->amount === $other->amount && $this->currency === $other->currency;\n    }\n}",
    "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",
    "severity": "medium",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-25",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/value_object",
        "html_url": "https://codeclaritylab.com/glossary/value_object",
        "json_url": "https://codeclaritylab.com/glossary/value_object.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Value Object](https://codeclaritylab.com/glossary/value_object) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/value_object"
            }
        }
    }
}