Domain-Driven Design - Entities in TypeScript

Before you look at the Entities example in TypeScript, you can see at my post on what Entities are in Domain-Driven Design.

Entity base class in TypeScript

abstract class Entity {
  protected readonly _id: UniqueId;

  protected constructor(id: UniqueId) {
    if (!id)
      throw new InvalidUniqueIdError();

    this._id = id;
  }

  public equals(other?: Entity): boolean {
    if (other === null || other === undefined) {
      return false;
    }

    return this._id.equals(other._id);
  }
}

About this code snippet:

  • It is an abstract class: This prevents this class from being instantiated and allows to declare abstract methods.
  • It have a protected readonly id property, so this property can only be populated within the constructor of the class that implements it.
  • The constructor validates if the id parameter is valid, and if not, it throws an exception, so no entities are created with an invalid id property.
  • It have an equals method to verify equality with other entities by checking its id property.

Entity example in TypeScript

class Product extends Entity {
  private _price: Price;

  private constructor(id: UniqueId, price: Price) {
    super(id);

    if (!price)
      throw new InvalidPriceError();

    this._price = price;
  }

  static create(id: UniqueId, price: Price): Product {
    return new Product(id, price);
  }

  public get id(): UniqueId {
    return this._id;
  }

  public get price(): Price {
    return this._price;
  }

  public updatePrice(price: Price): void {
    if (!price) throw new InvalidPriceError();

    this._price = price;
  }
}

About this code snippet:

  • It has private properties.
  • Its properties are Value Objects.
  • The constructor validates if the price parameter is valid, and if not, it throws an exception, so no entities are created with an invalid price property.
  • No public constructor: Other classes cannot create instances of this class, except for nested classes.
  • To create instances of this Value Object we do not call the constructor directly, but it uses the Static Factory Method pattern instead.
  • It have public getters to read the current state of the entity.
  • It have public methods to change the value of the entity’s properties.