Skip to main content

Value Object

Value Objects are one of the fundamental building blocks of Domain-Driven Design (DDD).

They encapsulate primitive values and enforce related invariants, ensuring data integrity within your domain.

In the DDD book, they are defined as follows:

"An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are."

-- Eric Evans

Unlike entities, value objects do not have a concept of identity; they only encapsulate and wrap primitive types.

There are three main qualities of Value Objects:

  1. Immutability: Once created, a value object cannot be modified.
  2. No Identity: Value objects are identified only by their attributes, not by a unique ID.
  3. Structural Equality: Two value objects are considered equal if their attributes are the same.

Value Object Implementation

An example of a value object can be found at: Task/src/shared/domain/valueObjects/TitleValueObject.ts

import { ValueObject, ValidationError, OverwriteProtectionBody, shallowEqualObject } from "@codebricks/codebricks-framework";
import { isType } from "is-what";

export class TitleValueObject implements ValueObject {
constructor(readonly _value: string) {
this.validate(_value);
}

@OverwriteProtectionBody(false)
validate(value: string): void {
if (!isType(value, String) || !value.length) {
throw new ValidationError(`TitleValueObject: ${value} is invalid`);
}
}

@OverwriteProtectionBody(false)
equals(other: ValueObject): boolean {
return other && isType(other, TitleValueObject) && shallowEqualObject(this, other);
}

get value(): string {
return this._value;
}
}

Detailed Implementation

_value Property

The _value property stores the encapsulated primitive value. It is marked as readonly to enforce immutability.

validate Function

The validate function ensures that the value meets the required invariants. If the value does not meet these criteria, a ValidationError is thrown.

@OverwriteProtectionBody(false)
validate(value: string): void {
if (!isType(value, String) || !value.length) {
throw new ValidationError(`TitleValueObject: ${value} is invalid`);
}
}

equals Function

The equals function checks if another value object is equal to the current one by comparing their values.

@OverwriteProtectionBody(false)
equals(other: ValueObject): boolean {
return other && isType(other, TitleValueObject) && shallowEqualObject(this, other);
}

Comparison to Entities

Value objects differ from entities in that they lack a unique identity and are defined by their attributes. Entities, on the other hand, have a unique identity and their equality is based on this identity.

Best Practices

  • Immutability: Ensure value objects are immutable to maintain integrity.
  • Validation: Always validate the value within the constructor to enforce invariants.
  • Fine Granularity: Stay with your business granularity just because two value objects share a similar pattern does not mean it does not evolve differently.