用于领域驱动设计 (DDD) 的 TypeScript
领域驱动设计 (DDD) 是一种强大的方法,通过专注于核心业务领域及其相关逻辑来处理复杂的软件系统。TypeScript 具有强大的类型和现代功能,是有效实现 DDD 概念的绝佳工具。本文探讨了 TypeScript 和 DDD 之间的协同作用,提供了实用的见解、策略和示例,以弥合设计和代码之间的差距。
理解领域驱动设计
**核心概念**
**1. 无处不在的语言**
开发人员和领域专家使用共享语言进行协作,以减少沟通错误。
**2. 有界上下文**
明确分离领域的不同部分,确保特定环境中的自主性和清晰度。
**3. 实体和值对象**
**4. 聚合**
域对象集群被视为数据变更的单个单元。
**5. 存储库**
抽象持久性逻辑,提供对聚合的访问。
**6. 域事件**
当域内发生重大动作时发出的信号。
**7. 应用服务**
封装业务工作流和编排逻辑。
为什么 TypeScript 适合 DDD
**1. 静态类型**:强类型检查有助于明确地建模域逻辑。
**2. 接口**:强制执行组件之间的契约。
**3. 类:**自然地表示实体、值对象和聚合。
**4. 类型保护**:确保运行时的类型安全。
**5. 实用类型**:为动态域启用强大的类型转换。
实际实施
**1. 建模实体**
实体具有唯一的身份并封装行为。
class Product {
constructor(
private readonly id: string,
private name: string,
private price: number
) {}
changePrice(newPrice: number): void {
if (newPrice <= 0) {
throw new Error("Price must be greater than zero.");
}
this.price = newPrice;
}
getDetails() {
return { id: this.id, name: this.name, price: this.price };
}
}**2. 创建值对象**
值对象是不可变的,并且通过值进行比较。
class Money {
constructor(private readonly amount: number, private readonly currency: string) {
if (amount < 0) {
throw new Error("Amount cannot be negative.");
}
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error("Currency mismatch.");
}
return new Money(this.amount + other.amount, this.currency);
}
}**3. 定义聚合**
聚合确保边界内的数据一致性。
class Order {
private items: OrderItem[] = [];
constructor(private readonly id: string) {}
addItem(product: Product, quantity: number): void {
const orderItem = new OrderItem(product, quantity);
this.items.push(orderItem);
}
calculateTotal(): number {
return this.items.reduce((total, item) => total + item.getTotalPrice(), 0);
}
}
class OrderItem {
constructor(private product: Product, private quantity: number) {}
getTotalPrice(): number {
return this.product.getDetails().price * this.quantity;
}
}**4. 实现存储库**
存储库抽象数据访问。
interface ProductRepository {
findById(id: string): Product | null;
save(product: Product): void;
}
class InMemoryProductRepository implements ProductRepository {
private products: Map = new Map();
findById(id: string): Product | null {
return this.products.get(id) || null;
}
save(product: Product): void {
this.products.set(product.getDetails().id, product);
}
} **5. 使用领域事件**
领域事件通知系统状态变化。
class DomainEvent {
constructor(public readonly name: string, public readonly occurredOn: Date) {}
}
class OrderPlaced extends DomainEvent {
constructor(public readonly orderId: string) {
super("OrderPlaced", new Date());
}
}
// Event Handler Example
function onOrderPlaced(event: OrderPlaced): void {
console.log(`Order with ID ${event.orderId} was placed.`);
}**6. 应用服务**
应用服务协调工作流程并执行用例。
class OrderService {
constructor(private orderRepo: OrderRepository) {}
placeOrder(order: Order): void {
this.orderRepo.save(order);
const event = new OrderPlaced(order.id);
publishEvent(event); // Simulated event publishing
}
}7. 使用有界上下文
利用 TypeScript 的模块化功能来隔离有界上下文。
**示例结构:**
/src
/sales
- Product.ts
- Order.ts
- ProductRepository.ts
/inventory
- Stock.ts
- StockService.ts
/shared
- DomainEvent.ts高级功能
**灵活建模的条件类型**
type Response= T extends "success" ? { data: any } : { error: string };
**用于验证的模板文字类型**
type Currency = `${"USD" | "EUR" | "GBP"}`;