当前位置: 首页 > news >正文

前端领域驱动设计:构建业务聚焦的应用架构

前端领域驱动设计:构建业务聚焦的应用架构

前言

嘿,各位前端小伙伴!今天我们来聊聊领域驱动设计(Domain-Driven Design,简称DDD)。如果你之前主要关注UI层的开发,那么DDD可能会为你打开一扇新的大门。

领域驱动设计的核心思想是:将业务领域作为软件设计的核心。想象一下,你的应用就像一座城市,业务领域就是城市的规划蓝图。有了这个蓝图,各个组件就能和谐协作,共同实现业务目标。

一、领域驱动设计基础

1.1 核心概念

DDD包含几个核心概念:

  1. 领域(Domain):业务所涉及的范围和知识
  2. 领域模型(Domain Model):对领域的抽象表示
  3. 实体(Entity):具有唯一标识的业务对象
  4. 值对象(Value Object):描述性的对象,没有唯一标识
  5. 聚合(Aggregate):一组相关对象的组合
  6. 聚合根(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): Promise<void>; findById(id: string): Promise<Order | null>; findByUserId(userId: string): Promise<Order[]>; } interface PaymentService { processPayment(orderId: string, amount: Money): Promise<boolean>; } class OrderService { constructor( private orderRepository: OrderRepository, private paymentService: PaymentService ) {} async createOrder(userId: string, items: OrderItem[]): Promise<OrderAggregate> { 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): Promise<void> { 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[]): Promise<OrderAggregate> { // ... 创建订单逻辑 // 发布领域事件 this.eventPublisher.publish(new OrderCreatedEvent(order.id, userId, items)); return aggregate; } async payOrder(orderId: string): Promise<void> { // ... 支付逻辑 // 发布领域事件 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): Promise<CreateOrderOutput> { 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): Promise<void> { await fetch('/api/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(order) }); } async findById(id: string): Promise<Order | null> { const response = await fetch(`/api/orders/${id}`); return response.ok ? response.json() : null; } async findByUserId(userId: string): Promise<Order[]> { const response = await fetch(`/api/orders?userId=${userId}`); return response.json(); } } class StripePaymentService implements PaymentService { async processPayment(orderId: string, amount: Money): Promise<boolean> { 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 }>) => Promise<void>; } export function OrderForm({ onSubmit }: OrderFormProps) { const [items, setItems] = useState<Array<{ 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 type="text" placeholder="商品ID" value={productId} onChange={(e) => setProductId(e.target.value)} /> <input type="number" placeholder="数量" value={quantity} onChange={(e) => setQuantity(parseInt(e.target.value) || 1)} /> <input type="number" placeholder="价格" value={price} onChange={(e) => setPrice(parseFloat(e.target.value) || 0)} /> <button type="button" 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 type="button" onClick={() => handleRemoveItem(index)}>删除</button> </div> ))} </div> <div> <strong>总计: {total}</strong> </div> <button type="submit" 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): Promise<void> { // 数据库操作 } }

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'; } } }

八、总结

领域驱动设计为前端应用提供了一种业务聚焦的架构方式:

  1. 领域层:包含实体、值对象、聚合和业务规则
  2. 应用层:编排用例,协调领域层和基础设施层
  3. 基础设施层:实现持久化、外部服务调用等技术细节
  4. UI层:展示数据和接收用户输入

通过将业务逻辑从技术细节中分离出来,DDD帮助我们构建:

  • 业务聚焦:代码直接反映业务规则
  • 可测试:各层可以独立测试
  • 可维护:业务逻辑集中,易于理解和修改
  • 可扩展:添加新功能只需在相应层添加代码

如果你正在开发一个业务复杂的前端应用,DDD是一个值得考虑的架构选择!

延伸阅读

  • Domain-Driven Design by Eric Evans
  • Implementing Domain-Driven Design by Vaughn Vernon
  • DDD in TypeScript

如果你喜欢这篇文章,请点赞、收藏、关注三连!你的支持是我创作的最大动力!🚀

http://www.cnnetsun.cn/news/2650209.html

相关文章:

  • 别再用ChatGPT了!手把手教你用FLAN-T5微调自己的客服聊天摘要助手(附DialogSum数据集实战)
  • STM32 CubeMX + HAL库实战:5分钟搞定GPIO配置并读懂自动生成的代码
  • 保姆级教程:用Docker部署OnlyOffice并集成到Cloudreve,实现文档在线预览(附完整代码)
  • AI在ABM营销中的实战应用:从数据整合到个性化策略
  • 【仅限本周开放】Claude蒙特卡洛模拟私密训练手册(含21个真实故障日志+对应修复Prompt模板+收敛阈值计算表)
  • 汽车电子工程师必看:ISO 16750-2023全套标准解读与实战应用避坑指南
  • 从SENet到ConvNeXt:聊聊那些‘小改动大提升’的经典网络设计(以SE模块为例)
  • 机器学习实战:四步框架让业务人员也能构建预测模型
  • 从PID调参到AI决策:手把手教你用Arduino Mega 2560和Jetson Nano打造一辆能“思考”的小车
  • Claude服务蓝图设计实战手册:从零搭建企业级AI服务架构的5个关键决策点
  • LIO-SAM 完整安装教程(Ubuntu 20.04 + ROS Noetic + GTSAM 4.0)
  • A51汇编器预定义宏在8051开发中的应用与技巧
  • 如何快速上手MindSpore-Lab/bert-base-uncased:从安装到第一个掩码语言模型的完整教程
  • 解锁本地AI语音识别的革命性体验:OBS LocalVocal插件深度解析
  • 无人机集群分布式模型预测控制技术解析
  • GPU性能优化:硬件感知LLM技术SwizzlePerf解析
  • 机器学习本质探析:从数据拟合到模型泛化的认知边界
  • 给嵌入式新手的保姆级指南:手把手教你用设备树配置i.MX6ULL的引脚(pinctrl实战)
  • 告别默认布局:在UE4.27中为你的本地多人游戏打造专属分屏体验(C++/蓝图混合教程)
  • AI可控性实战:编译规则引擎如何驯服大模型输出
  • Llama-medx_v2社区贡献指南:如何参与医疗AI开源项目的开发与改进
  • MODBUS、USB、XMODEM...一文搞懂CRC16的7种标准到底怎么选(附C代码实测对比)
  • GovernanceBERT-base API完全指南:10个实用调用示例
  • HVV期间,红队最爱打的漏洞Top 10:从告警日志看实战攻击手法(附CVE编号)
  • QuickBMS终极指南:轻松提取游戏资源的开源利器
  • RapidIO网络实战:在Linux 5.4下用rionet.ko搭建板间高速以太网通道
  • 2019网页设计趋势实战复盘:从暗黑模式到3D交互的深度解析
  • 如何快速搭建个人数字书库:Talebook完整安装指南
  • 避开WS2812B的时序坑:STM32F103C8T6用PWM+DMA驱动的实测避坑指南
  • 立体视觉拯救者:用3Dmigoto彻底修复游戏破碎3D效果