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 ProductIdAll lessons in this course
- Branding/opaque types to prevent unit mixups
- Tagged IDs and domain modeling patterns
- Domain modeling — aggregates, invariants & services