Create Value Objects
Design and implement immutable value objects for a new domain model. Use when defining domain concepts represented by identifiers, names, money, quantities, measurements, date ranges, addresses, or other values that need explicit meaning, validation, normalization, equality, or cohesive behaviour from the outset.
Start from the domain language
- Collect concrete business scenarios and the terms domain experts use.
- Identify values whose meaning includes rules, units, formats, or context.
- Write each concept's invariants before choosing its fields or API.
- Decide which values form one indivisible concept, such as amount and currency or a date range's start and end.
- Keep a primitive when it has no domain-specific rules or ambiguity.
Design each value object
- Name it using the ubiquitous language.
- Make valid instances immutable.
- Provide controlled construction that validates the complete value.
- Normalize only when the domain says two representations mean the same thing.
- Define equality using the complete normalized value.
- Add behaviour that belongs to the concept, such as comparison, arithmetic, formatting, or range checks.
- Prevent accidental interchange between identifiers or measurements that share a primitive representation.
Prefer APIs that accept and return domain types. Avoid public setters and implicit primitive conversions that let callers bypass the model.
Place rules deliberately
Put rules intrinsic to one value inside that value object. Put rules involving an entity's lifecycle or several independent concepts in the entity, aggregate, or domain service responsible for the decision.
Use the codebase's established result type for expected validation failures. Reserve exceptions for programmer errors or when the host architecture intentionally uses them for construction failures.
Connect the model
- Use value objects in commands, aggregate operations, events, and domain-facing interfaces from the beginning.
- Keep transport, serialization, and persistence mappings explicit at system boundaries.
- Ensure frameworks cannot create invalid instances through parameterless construction or public mutation.
- Test valid creation, rejected values, boundaries, normalization, equality, and domain behaviour.
Deliver
Provide the domain vocabulary, selected value objects, invariants, construction APIs, behaviour, boundary mappings, and tests. Explain why any plausible concept remains a primitive.