0Pricing
TypeScript Academy · Lesson

Domain modeling — aggregates, invariants & services

Model aggregates like Cart with branded IDs and enforce invariants via factories and services.

Intro

Goal: Model a Cart aggregate using branded IDs and ensure rules like qty > 0 are enforced in factories and services.

  • Branded IDs distinguish entity boundaries
  • Factories return Result unions
  • Service functions avoid invalid transitions

Entities & brands

Branded IDs make CartId and ProductId incompatible even though both are strings. This prevents mixups.

type Brand<Tag extends string, T> = T & { readonly __brand: Tag }

type CartId = Brand<"CartId", string>
type ProductId = Brand<"ProductId", string>

type Result<T> = { ok: true; value: T } | { ok: false; error: string }

const cartId = (s: string): CartId => s as CartId
const productId = (s: string): ProductId => s as ProductId

All lessons in this course

  1. Branding/opaque types to prevent unit mixups
  2. Tagged IDs and domain modeling patterns
  3. Domain modeling — aggregates, invariants & services
← Back to TypeScript Academy