前端领域驱动设计构建业务聚焦的应用架构前言嘿各位前端小伙伴今天我们来聊聊领域驱动设计Domain-Driven Design简称DDD。如果你之前主要关注UI层的开发那么DDD可能会为你打开一扇新的大门。领域驱动设计的核心思想是将业务领域作为软件设计的核心。想象一下你的应用就像一座城市业务领域就是城市的规划蓝图。有了这个蓝图各个组件就能和谐协作共同实现业务目标。一、领域驱动设计基础1.1 核心概念DDD包含几个核心概念领域Domain业务所涉及的范围和知识领域模型Domain Model对领域的抽象表示实体Entity具有唯一标识的业务对象值对象Value Object描述性的对象没有唯一标识聚合Aggregate一组相关对象的组合聚合根Aggregate Root聚合的入口点// 实体示例用户 interface User { id: string; // 唯一标识 name: string; email: string; createdAt: Date; } // 值对象示例地址 interface Address { street: string; city: string; postalCode: string; country: string; } // 值对象应该是不可变的 class Money { constructor(public readonly amount: number, public readonly currency: string) {} add(other: Money): Money { if (this.currency ! other.currency) { throw new Error(货币类型不匹配); } return new Money(this.amount other.amount, this.currency); } }1.2 聚合与聚合根interface OrderItem { productId: string; quantity: number; price: Money; } interface Order { id: string; userId: string; items: OrderItem[]; total: Money; status: pending | paid | shipped | delivered; createdAt: Date; } class OrderAggregate { private order: Order; constructor(order: Order) { this.order order; } get id(): string { return this.order.id; } addItem(item: OrderItem): void { if (this.order.status ! pending) { throw new Error(只能向待处理订单添加商品); } this.order.items.push(item); this.updateTotal(); } removeItem(productId: string): void { if (this.order.status ! pending) { throw new Error(只能从待处理订单移除商品); } this.order.items this.order.items.filter(item item.productId ! productId); this.updateTotal(); } pay(): void { if (this.order.status ! pending) { throw new Error(只能支付待处理订单); } if (this.order.items.length 0) { throw new Error(订单没有商品); } this.order.status paid; } private updateTotal(): void { this.order.total this.order.items.reduce((sum, item) sum.add(item.price), new Money(0, CNY) ); } }二、领域服务2.1 业务逻辑封装interface OrderRepository { save(order: Order): Promisevoid; findById(id: string): PromiseOrder | null; findByUserId(userId: string): PromiseOrder[]; } interface PaymentService { processPayment(orderId: string, amount: Money): Promiseboolean; } class OrderService { constructor( private orderRepository: OrderRepository, private paymentService: PaymentService ) {} async createOrder(userId: string, items: OrderItem[]): PromiseOrderAggregate { const order: Order { id: crypto.randomUUID(), userId, items: [], total: new Money(0, CNY), status: pending, createdAt: new Date() }; const aggregate new OrderAggregate(order); items.forEach(item aggregate.addItem(item)); await this.orderRepository.save(aggregate[order]); return aggregate; } async payOrder(orderId: string): Promisevoid { const order await this.orderRepository.findById(orderId); if (!order) { throw new Error(订单不存在); } const aggregate new OrderAggregate(order); const success await this.paymentService.processPayment( orderId, aggregate[order].total ); if (success) { aggregate.pay(); await this.orderRepository.save(aggregate[order]); } } }三、领域事件3.1 事件驱动的业务流程interface DomainEvent { eventId: string; aggregateId: string; eventType: string; timestamp: Date; } class OrderCreatedEvent implements DomainEvent { eventId: string; timestamp: Date; constructor( public readonly aggregateId: string, public readonly userId: string, public readonly items: OrderItem[] ) { this.eventId crypto.randomUUID(); this.timestamp new Date(); this.eventType OrderCreated; } } class OrderPaidEvent implements DomainEvent { eventId: string; timestamp: Date; constructor(public readonly aggregateId: string) { this.eventId crypto.randomUUID(); this.timestamp new Date(); this.eventType OrderPaid; } } interface DomainEventPublisher { publish(event: DomainEvent): void; } class OrderService { constructor( private orderRepository: OrderRepository, private paymentService: PaymentService, private eventPublisher: DomainEventPublisher ) {} async createOrder(userId: string, items: OrderItem[]): PromiseOrderAggregate { // ... 创建订单逻辑 // 发布领域事件 this.eventPublisher.publish(new OrderCreatedEvent(order.id, userId, items)); return aggregate; } async payOrder(orderId: string): Promisevoid { // ... 支付逻辑 // 发布领域事件 this.eventPublisher.publish(new OrderPaidEvent(orderId)); } }四、应用层4.1 用例编排interface CreateOrderInput { userId: string; items: Array{ productId: string; quantity: number; price: number }; } interface CreateOrderOutput { orderId: string; total: number; status: string; } class CreateOrderUseCase { constructor(private orderService: OrderService) {} async execute(input: CreateOrderInput): PromiseCreateOrderOutput { const orderItems: OrderItem[] input.items.map(item ({ productId: item.productId, quantity: item.quantity, price: new Money(item.price, CNY) })); const order await this.orderService.createOrder(input.userId, orderItems); return { orderId: order.id, total: order[order].total.amount, status: order[order].status }; } }五、基础设施层5.1 持久化实现class DatabaseOrderRepository implements OrderRepository { async save(order: Order): Promisevoid { await fetch(/api/orders, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(order) }); } async findById(id: string): PromiseOrder | null { const response await fetch(/api/orders/${id}); return response.ok ? response.json() : null; } async findByUserId(userId: string): PromiseOrder[] { const response await fetch(/api/orders?userId${userId}); return response.json(); } } class StripePaymentService implements PaymentService { async processPayment(orderId: string, amount: Money): Promiseboolean { const response await fetch(/api/payments/stripe, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ orderId, amount: amount.amount, currency: amount.currency }) }); const result await response.json(); return result.success; } }六、UI层集成6.1 React组件示例interface OrderFormProps { onSubmit: (items: Array{ productId: string; quantity: number; price: number }) Promisevoid; } export function OrderForm({ onSubmit }: OrderFormProps) { const [items, setItems] useStateArray{ productId: string; quantity: number; price: number }([]); const [productId, setProductId] useState(); const [quantity, setQuantity] useState(1); const [price, setPrice] useState(0); const handleAddItem () { if (!productId || price 0) return; setItems([...items, { productId, quantity, price }]); setProductId(); setQuantity(1); setPrice(0); }; const handleRemoveItem (index: number) { setItems(items.filter((_, i) i ! index)); }; const handleSubmit async (e: React.FormEvent) { e.preventDefault(); if (items.length 0) return; try { await onSubmit(items); setItems([]); } catch (error) { console.error(创建订单失败:, error); } }; const total items.reduce((sum, item) sum item.price * item.quantity, 0); return ( form onSubmit{handleSubmit} div input typetext placeholder商品ID value{productId} onChange{(e) setProductId(e.target.value)} / input typenumber placeholder数量 value{quantity} onChange{(e) setQuantity(parseInt(e.target.value) || 1)} / input typenumber placeholder价格 value{price} onChange{(e) setPrice(parseFloat(e.target.value) || 0)} / button typebutton onClick{handleAddItem}添加商品/button /div div h3订单商品/h3 {items.map((item, index) ( div key{index} span{item.productId} x {item.quantity} {item.price * item.quantity}/span button typebutton onClick{() handleRemoveItem(index)}删除/button /div ))} /div div strong总计: {total}/strong /div button typesubmit disabled{items.length 0}创建订单/button /form ); }6.2 使用案例// 依赖注入 const orderRepository new DatabaseOrderRepository(); const paymentService new StripePaymentService(); const eventPublisher new InMemoryEventPublisher(); const orderService new OrderService(orderRepository, paymentService, eventPublisher); const createOrderUseCase new CreateOrderUseCase(orderService); // UI组件 function OrderPage() { const handleCreateOrder async (items: Array{ productId: string; quantity: number; price: number }) { const result await createOrderUseCase.execute({ userId: current-user-id, items }); console.log(订单创建成功:, result); }; return ( div h1创建订单/h1 OrderForm onSubmit{handleCreateOrder} / /div ); }七、DDD优势7.1 业务聚焦DDD将业务逻辑从技术细节中分离出来// 业务规则在领域层 class OrderAggregate { pay(): void { if (this.order.status ! pending) { throw new Error(只能支付待处理订单); } // ... } } // 技术实现在基础设施层 class DatabaseOrderRepository implements OrderRepository { async save(order: Order): Promisevoid { // 数据库操作 } }7.2 可测试性describe(OrderService, () { it(should create an order, async () { const mockRepo: OrderRepository { save: jest.fn().mockResolvedValue(), findById: jest.fn().mockResolvedValue(null), findByUserId: jest.fn().mockResolvedValue([]) }; const mockPaymentService: PaymentService { processPayment: jest.fn().mockResolvedValue(true) }; const mockPublisher: DomainEventPublisher { publish: jest.fn() }; const service new OrderService(mockRepo, mockPaymentService, mockPublisher); const order await service.createOrder(user1, [ { productId: p1, quantity: 2, price: new Money(100, CNY) } ]); expect(mockRepo.save).toHaveBeenCalled(); expect(mockPublisher.publish).toHaveBeenCalledWith(expect.any(OrderCreatedEvent)); }); });7.3 可维护性业务规则集中在领域层易于理解和修改// 如果业务规则改变只需修改领域层 class OrderAggregate { // 新增规则订单金额超过1000元需要审核 pay(): void { if (this.order.status ! pending) { throw new Error(只能支付待处理订单); } if (this.order.total.amount 1000) { this.order.status pending_review; } else { this.order.status paid; } } }八、总结领域驱动设计为前端应用提供了一种业务聚焦的架构方式领域层包含实体、值对象、聚合和业务规则应用层编排用例协调领域层和基础设施层基础设施层实现持久化、外部服务调用等技术细节UI层展示数据和接收用户输入通过将业务逻辑从技术细节中分离出来DDD帮助我们构建业务聚焦代码直接反映业务规则可测试各层可以独立测试可维护业务逻辑集中易于理解和修改可扩展添加新功能只需在相应层添加代码如果你正在开发一个业务复杂的前端应用DDD是一个值得考虑的架构选择延伸阅读Domain-Driven Design by Eric EvansImplementing Domain-Driven Design by Vaughn VernonDDD in TypeScript如果你喜欢这篇文章请点赞、收藏、关注三连你的支持是我创作的最大动力