正在加载,请稍候…

领域驱动设计(DDD)与TypeScript:实践指南

在TypeScript项目中应用DDD概念。通过实际示例学习实体、值对象、聚合、领域服务和限界上下文。

领域驱动设计(DDD)与TypeScript:实践指南

领域驱动设计(DDD)与TypeScript

DDD 聚焦于核心领域和领域逻辑,基于领域模型进行复杂设计。

构建块

领域驱动设计(DDD)与TypeScript:实践指南 插图

值对象(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}`;
  }
}

领域驱动设计(DDD)与TypeScript:实践指南 插图

实体(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')
    );
  }
}

领域驱动设计(DDD)与TypeScript:实践指南 插图

聚合(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 在复杂业务领域中价值最大,因为逻辑是难点。