Entity vs Value Object in TypeScript: Structural Differences and Implementation (DDD)

A breakdown of the structural differences and implementation patterns between Entities and Value Objects in Domain-Driven Design (DDD).

While both are objects containing properties and methods, they serve distinct roles regarding identity (IDs) and mutability.

1. Entity

An Entity is an object uniquely identified by an ID. Even if its internal attributes (such as name or email) change, it remains the same object as long as the ID matches. Its properties are encapsulated and modified strictly through explicit methods.

// Entity Definition: User
export class User {
  // ① Identifier (ID): Unique key to distinguish the entity. Cannot be changed once set.
  readonly id: string;

  // Keep properties private to prevent direct external modification (Encapsulation)
  private name: string;
  private email: Email; // Holds a "Value Object (Email)" as a property

  constructor(id: string, name: string, email: Email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  // ② State modification method: Update name
  // Prevents direct assignment (e.g., user.name = "") and enforces logic through this method
  changeName(newName: string): void {
    // Guard clause to enforce domain rules and prevent invalid state
    if (newName.length === 0) {
      throw new Error("Name cannot be empty");
    }
    // Changing the name does not alter this.id (the identifier)
    this.name = newName;
  }

  // ② State modification method: Update email
  changeEmail(newEmail: Email): void {
    // Replaces the property with a new Value Object instance
    this.email = newEmail;
  }
}

2. Value Object

A Value Object has no identity (ID) and is defined solely by its values. It is immutable after creation and enforces self-validation upon instantiation to prevent invalid values from existing.

// Value Object Definition: Email
export class Email {
  // ① Stored value: Marked as readonly to guarantee immutability after creation
  readonly value: string;

  constructor(value: string) {
    // Self-defense validation on instantiation
    // Throws an error to prevent creating an instance with an invalid value
    if (!value.includes('@')) {
      throw new Error("Invalid email format");
    }
    this.value = value;
  }

  // ② Equality check method: Compares equality based on values
  // Since it has no ID, equality is determined by matching property values
  equals(other: Email): boolean {
    return this.value === other.value;
  }
}

3. Structural Differences

Key structural differences between Entities and Value Objects:

  • Identifier (ID): Entities have an ID; Value Objects do not.
  • Mutability: Entities are mutable via state-changing methods. Value Objects are immutable.
  • Structural Relationship: Entities manage identity and can contain Value Objects as properties.
  • Role: Entities manage individual lifecycles and state transitions. Value Objects ensure data type correctness and domain invariants.

Leave a Comment

CAPTCHA