
领域驱动设计(DDD)与TypeScript
DDD 聚焦于核心领域和领域逻辑,基于领域模型进行复杂设计。
构建块

值对象(Value Objects)
通过属性定义且不可变的对象,没有身份标识。
class Money {
private constructor(
readonly amount: number,
readonly currency: string
) {}
static of(amount: number, currency: string): Money {
if (amount < 0) throw new Error('Amount cannot be negative');
if (!currency || currency.length !== 3) throw new Error('Invalid currency code');
return new Money(amount, currency);
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error('Cannot add different currencies');
}
return Money.of(this.amount + other.amount, this.currency);
}
equals(other: Money): boolean {
return this.amount === other.amount && this.currency === other.currency;
}
toString(): string {
return `${this.amount} ${this.currency}`;
}
}

实体(Entities)
具有唯一标识且随时间持续存在的对象。
abstract class Entity<T> {
constructor(protected readonly id: T) {}
equals(other: Entity<T>): boolean {
return this.id === other.id;
}
}
class Order extends Entity<string> {
private _status: OrderStatus = 'pending';
private _items: OrderItem[] = [];
constructor(
id: string,
private readonly customerId: string,
private readonly createdAt: Date = new Date()
) {
super(id);
}
addItem(product: Product, quantity: number): void {
if (this._status !== 'pending') {
throw new Error('Cannot modify a non-pending order');
}
const existing = this._items.find(i => i.productId === product.id);
if (existing) {
existing.increaseQuantity(quantity);
} else {
this._items.push(new OrderItem(product.id, product.price, quantity));
}
}
confirm(): void {
if (this._items.length === 0) throw new Error('Order has no items');
if (this._status !== 'pending') throw new Error('Order already confirmed');
this._status = 'confirmed';
}
get total(): Money {
return this._items.reduce(
(sum, item) => sum.add(item.subtotal),
Money.of(0, 'USD')
);
}
}
聚合(Aggregates)
实体和值对象的集群,通过一个根实体控制访问。
// Order 是聚合根
class OrderAggregate {
private events: DomainEvent[] = [];
static create(customerId: string): OrderAggregate {
const order = new OrderAggregate(
generateId(),
customerId,
new Date()
);
order.addEvent(new OrderCreatedEvent(order.id, customerId));
return order;
}
ship(trackingCode: string): void {
if (this._status !== 'confirmed') {
throw new Error('Order must be confirmed before shipping');
}
this._status = 'shipped';
this._trackingCode = trackingCode;
this.addEvent(new OrderShippedEvent(this.id, trackingCode));
}
private addEvent(event: DomainEvent): void {
this.events.push(event);
}
getUncommittedEvents(): DomainEvent[] {
return [...this.events];
}
clearEvents(): void {
this.events = [];
}
}
领域服务(Domain Services)
不适合放在实体或值对象中的操作。
class OrderPricingService {
constructor(
private discountRepository: DiscountRepository,
private taxService: TaxService
) {}
async calculateTotal(order: Order, customerId: string): Promise<Money> {
let total = order.subtotal;
// 应用客户折扣
const discounts = await this.discountRepository.findForCustomer(customerId);
for (const discount of discounts) {
total = discount.apply(total);
}
// 添加税费
const tax = await this.taxService.calculate(total, order.shippingAddress);
return total.add(tax);
}
}
限界上下文(Bounded Contexts)
┌─────────────────────┐ ┌─────────────────────┐
│ 订单上下文 │ │ 库存上下文 │
│ ───────────────── │ │ ─────────────────── │
│ Order │ │ Product │
│ OrderItem │◄──►│ Stock │
│ Customer (ref) │ │ Warehouse │
└─────────────────────┘ └─────────────────────┘
│
▼
┌─────────────────────┐
│ 支付上下文 │
│ ───────────────── │
│ Payment │
│ Invoice │
│ Customer (ref) │
└─────────────────────┘
DDD 在复杂业务领域中价值最大,因为逻辑是难点。