DDD-031:案例:电商订单系统 DDD 建模

发布时间:2026/6/25 14:21:14
DDD-031:案例:电商订单系统 DDD 建模 DDD-031:案例:电商订单系统 DDD 建模本章导读本章通过一个完整的电商订单系统案例,展示 DDD 从需求分析、领域建模到代码实现的完整过程。我们将深入探讨订单聚合的设计、状态管理、领域事件的应用,以及分层架构的具体实现。学习目标掌握从需求到领域模型的建模过程学会订单聚合的设计与实现理解订单状态机的设计方法前置知识DDD 聚合、实体、值对象基础DDD 领域事件机制Spring Boot 开发基础阅读时长约 60-75 分钟【案例背景】电商订单系统一、业务需求分析1.1 核心业务流程订单生命周期: ┌──────────────────────────────────────────────────────────────────┐ │ 订单状态流转图 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ 创建订单 │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ CREATED (待支付) ││ │ │ - 用户已下单,等待支付 ││ │ │ - 可取消、可支付 ││ │ └─────────────────────────────────────────────────────────────┘│ │ │ │ │ │ 支付成功 用户取消 │ │ │ │ │ │ ▼ ▼ │ │ ┌───────────────────────┐ ┌───────────────────────┐ │ │ │ PAID (已支付) │ │ CANCELLED (已取消) │ │ │ │ - 等待发货 │ │ - 订单已取消 │ │ │ │ - 可取消、可发货 │ │ - 可申请退款 │ │ │ └───────────────────────┘ └───────────────────────┘ │ │ │ │ │ 商家发货 │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ SHIPPED (已发货) ││ │ │ - 等待用户确认收货 ││ │ │ - 可确认收货、可申请退款 ││ │ └─────────────────────────────────────────────────────────────┘│ │ │ │ │ 用户确认 │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ DELIVERED (已收货) ││ │ │ - 交易完成 ││ │ │ - 可评价、可申请退款 ││ │ └─────────────────────────────────────────────────────────────┘│ │ │ └──────────────────────────────────────────────────────────────────┘1.2 核心业务规则规则编号规则描述适用状态R1订单项不能为空创建时R2订单金额 = 订单项单价 × 数量之和创建时R3只有待支付状态可以支付CREATEDR4已支付或待支付状态可以取消CREATED, PAIDR5已发货状态可以确认收货SHIPPEDR6每个订单项数量不能超过库存创建时二、领域建模过程2.1 识别聚合订单上下文聚合识别: ┌────────────────────────────────────────────────────────────────┐ │ Order Context │ ├────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Order 聚合 │ │ │ │ │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ │ │ Order │───│ OrderItem │ │ Shipping │ │ │ │ │ │ 聚合根 │ │ 内部实体 │ │ Address │ │ │ │ │ │ │ │ │ │ 值对象 │ │ │ │ │ └────────────┘ └────────────┘ └────────────┘ │ │ │ │ │ │ │ │ 边界: │ │ │ │ - Order 是聚合根 │ │ │ │ - OrderItem 是内部实体 │ │ │ │ - ShippingAddress 是值对象 │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ 引用其他聚合(只持有 ID): │ │ - CustomerId:客户聚合引用 │ │ - ProductId:商品聚合引用 │ │ - PaymentId:支付聚合引用 │ │ │ └────────────────────────────────────────────────────────────────┘2.2 实体与值对象设计Order 聚合内部结构: ┌────────────────────────────────────────────────────────────────┐ │ Order │ │ (Aggregate Root) │ ├────────────────────────────────────────────────────────────────┤ │ - id: OrderId // 聚合根标识 │ │ - customerId: CustomerId // 客户引用(ID) │ │ - items: ListOrderItem // 订单项列表 │ │ - status: OrderStatus // 订单状态 │ │ - totalAmount: Money // 订单金额 │ │ - shippingAddress: Address // 收货地址 │ │ - paymentId: PaymentId // 支付引用(ID) │ │ - paidAt: Instant // 支付时间 │ │ - shippedAt: Instant // 发货时间 │ │ - deliveredAt: Instant // 收货时间 │ │ - cancelledAt: Instant // 取消时间 │ │ - cancelReason: String // 取消原因 │ ├────────────────────────────────────────────────────────────────┤ │ + create(customerId, items, address): Order │ │ + pay(paymentId): void │ │ + ship(shippingId): void │ │ + confirmDelivery(): void │ │ + cancel(reason): void │ │ + addItem(item): void │ │ + removeItem(itemId): void │ └────────────────────────────────────────────────────────────────┘ │ │ 包含 ▼ ┌────────────────────────────────────────────────────────────────┐ │ OrderItem │ │ (Entity) │ ├────────────────────────────────────────────────────────────────┤ │ - id: OrderItemId │ │ - productId: ProductId // 商品引用(ID) │ │ - productName: String // 商品名称快照 │ │ - productImage: String // 商品图片快照 │ │ - unitPrice: Money // 单价快照 │ │ - quantity: int // 数量 │ │ - subtotal: Money // 小计 │ ├────────────────────────────────────────────────────────────────┤ │ + getSubtotal(): Money │ │ + updateQuantity(quantity): void │ └────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────┐ ┌─────────────────────────────┐ │ Address │ │ Money │ │ (Value Object) │ │ (Value Object) │ ├─────────────────────────────────┤ ├─────────────────────────────┤ │ - province: String │ │ - amount: BigDecimal │ │ - city: String │ │ - currency: Currency │ │ - district: String │ ├─────────────────────────────┤ │ - detail: String │ │ + add(other): Money │ │ - postalCode: String │ │ + multiply(factor): Money │ │ - receiver: String │ │ + subtract(other): Money │ │ - phone: String │ └─────────────────────────────┘ ├─────────────────────────────────┤ │ + getFullAddress(): String │ └─────────────────────────────────┘三、代码实现详解3.1 项目结构order-context/ ├── domain/ # 领域层 │ ├── model/ │ │ ├── order/ │ │ │ ├── Order.java # 订单聚合根 │ │ │ ├── OrderItem.java # 订单项实体 │ │ │ ├── OrderStatus.java # 订单状态枚举 │ │ │ └── OrderFactory.java # 订单工厂 │ │ └── shared/ │ │ ├── Money.java # 金额值对象 │ │ └── Address.java # 地址值对象 │ ├── event/ │ │ ├── OrderCreatedEvent.java │ │ ├── OrderPaidEvent.java │ │ ├── OrderShippedEvent.java │ │ └── OrderCancelledEvent.java │ ├── repository/ │ │ └── OrderRepository.java # 仓储接口 │ └── service/ │ └── OrderDomainService.java # 领域服务 ├── application/ # 应用层 │ ├── OrderApplicationService.java │ ├── command/ │ │ ├── CreateOrderCommand.java │ │ ├── PayOrderCommand.java │ │ └── CancelOrderCommand.java │ └── event/ │ └── OrderEventHandler.java ├── infrastructure/ # 基础设施层 │ ├── persistence/ │ │ ├── JpaOrderRepository.java │ │ ├── OrderPo.java # 持久化对象 │ │ └── OrderRepositoryImpl.java │ └── messaging/ │ └── OrderEventPublisher.java └── interface/ # 接口层 ├── controller/ │ └── OrderController.java ├── dto/ │ ├── CreateOrderRequest.java │ └── OrderResponse.java └── assembler/ └── OrderAssembler.java3.2 订单聚合实现// ✅ 订单聚合根实现packagecom.example.order.domain.model.order;publicclassOrderextendsAggregateRootOrderId{// 状态privateOrderStatusstatus;privateCustomerIdcustomerId;privateListOrderItemitems;privateMoneytotalAmount;privateAddressshippingAddress;// 支付信息privatePaymentIdpaymentId;privateInstantpaidAt;// 发货信息privateShippingIdshippingId;privateInstantshippedAt;// 收货信息privateInstantdeliveredAt;// 取消信息privateStringcancelReason;privateInstantcancelledAt;// ========== 工厂方法 ==========/** * 创建订单 */publicstaticOrdercreate(CustomerIdcustomerId,ListOrderItemitems,AddressshippingAddress){// 业务规则验证if(items==null||items.isEmpty()){thrownewIllegalArgumentException("订单项不能为空");}if(shippingAddress==null){thrownewIllegalArgumentException("收货地址不能为空");}// 创建订单Orderorder=newOrder();order.id=OrderId.generate();order.customerId=customerId;order.items=newArrayList(items);// 防御性复制order.shippingAddress=shippingAddress;order.status=OrderStatus.CREATED;order.totalAmount=calculateTotalAmount(items);order.createdAt=Instant.now();// 发布领域事件order.registerEvent(newOrderCreatedEvent(order.id,order.customerId,order.items,order.totalAmount,order.shippingAddress,order.createdAt));returnorder;}privatestaticMoneycalculateTotalAmount(ListOrderItemitems){returnitems.stream().map(OrderItem::getSubtotal).reduce(Money.ZERO,Money::add);}// ========== 业务方法 ==========/** * 支付订单 */publicvoidpay(PaymentIdpaymentId,InstantpaidAt){// 状态验证if(status!=OrderStatus.CREATED){thrownewInvalidOrderStateException(String.format("只有待支付状态的订单才能支付,当前状态:%s",status));}// 状态变更this.status=OrderStatus.PAID;this.paymentId=paymentId;this.paidAt=paidAt;// 发布事件registerEvent(newOrderPaidEvent(this.id,paymentId,paidAt));}/** * 发货 */publicvoidship(ShippingIdshippingId,InstantshippedAt){if(status!=OrderStatus.PAID){thrownewInvalidOrderStateException(String.format("只有已支付状态的订单才能发货,当前状态:%s",status));}this.status=OrderStatus.SHIPPED;this.shippingId=shippingId;this.shippedAt=shippedAt;registerEvent(newOrderShippedEvent(this.id,shippingId,shippedAt));}/** * 确认收货 */publicvoidconfirmDelivery(InstantdeliveredAt){if(status!=OrderStatus.SHIPPED){thrownewInvalidOrderStateException(String.format("只有已发货状态的订单才能确认收货,当前状态:%s",status));}this.status=OrderStatus.DELIVERED;this.deliveredAt=deliveredAt;registerEvent(newOrderDeliveredEvent(this.id,deliveredAt));}/** * 取消订单 */publicvoidcancel(Stringreason,InstantcancelledAt){if(status!=OrderStatus.CREATEDstatus!=OrderStatus.PAID){thrownewInvalidOrderStateException(String.format("只有待支付或已支付状态的订单才能取消,当前状态:%s",status));}OrderStatuspreviousStatus=this.status;this.status=OrderStatus.CANCELLED;this.cancelReason=reason;this.cancelledAt=cancelledAt;registerEvent(newOrderCancelledEvent(this.id,reason,previousStatus,cancelledAt));}/** * 添加订单项 */publicvoidaddItem(OrderItemitem){if(status!=OrderStatus.CREATED){thrownewInvalidOrderStateException("只有待支付状态的订单才能添加商品");}// 检查是否已存在相同商品OptionalOrderItemexisting=items.stream().filter(i-i.getProductId().equals(item.getProductId())).findFirst();if(existing.isPresent()){// 合并数量existing.get().increaseQuantity(item.getQuantity());}else{items.add(item);}// 重新计算总金额this.totalAmount=calculateTotalAmount(items);}/** * 移除订单项 */publicvoidremoveItem(OrderItemIditemId){if(status!=OrderStatus.CREATED){thrownewInvalidOrderStateException("只有待支付状态的订单才能移除商品");}booleanremoved=items.removeIf(item-item.getId().equals(itemId));if(!removed){thrownewIllegalArgumentException("订单项不存在:"+itemId);}if(items.isEmpty()){thrownewIllegalStateException("订单至少需要一个订单项");}// 重新计算总金额this.totalAmount=calculateTotalAmount(items);}// ========== Getters ==========publicOrderStatusgetStatus(){returnstatus;}publicCustomerIdgetCustomerId(){returncustomerId;}publicListOrderItemgetItems(){returnCollections.unmodifiableList(items);}publicMoneygetTotalAmount(){returntotalAmount;}publicAddressgetShippingAddress(){returnshippingAddress;}publicPaymentIdgetPaymentId(){returnpaymentId;}// 不提供 Setters,状态只能通过业务方法修改}3.3 订单项实体// ✅ 订单项实体publicclassOrderItemextendsEntityOrderItemId{privateProductIdproductId;privateStringproductName;// 快照privateStringproductImage;// 快照privateMoneyunitPrice;// 快照privateintquantity;// 私有构造器,通过工厂方法创建privateOrderItem(){}publicstaticOrderItemcreate(ProductIdproductId,StringproductName,StringproductImage,MoneyunitPrice,intquantity){if(quantity=0){thrownewIllegalArgumentException("数量必须大于0");}OrderItemitem=newOrderItem();item.id=OrderItemId.generate();item.productId=productId;item.productName=productName;item.productImage=productImage;item.unitPrice=unitPrice;item.quantity=quantity;returnitem;}publicMoneygetSubtotal(){returnunitPrice.multiply(quantity);}publicvoidincreaseQuantity(intdelta){if(delta=0){thrownewIllegalArgumentException("增量必须大于0");}this.quantity+=delta;}publicvoiddecreaseQuantity(intdelta){if(delta=0||delta=this.quantity){thrownewIllegalArgumentException("减量无效");}this.quantity-=delta;}// Getters...}3.4 应用服务实现// ✅ 订单应用服务@Service@TransactionalpublicclassOrderApplicationService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateProductGatewayproductGateway;@AutowiredprivateDomainEventPublishereventPublisher;/** * 创建订单 */publicOrderIdcreateOrder(CreateOrderCommandcommand){// 1. 构建订单项ListOrderItemitems=newArrayList();for(CreateOrderCommand.ItemDtoitemDto:command.getItems()){// 查询商品信息(通过网关)ProductInfoproduct=productGateway.getProduct(itemDto.getProductId());OrderItemitem=OrderItem.create(ProductId.of(itemDto.getProductId()),product.getName(),product.getImage(),Money.of(product.getPrice()),itemDto.getQuantity());items.add(item);}// 2. 构建收货地址AddressshippingAddress=newAddress(command.getAddress().getProvince(),command.getAddress().getCity(),command.getAddress().getDistrict(),command.getAddress().getDetail(),command.getAddress().getPostalCode(),command.getAddress().getReceiver(),command.getAddress().getPhone());// 3. 创建订单(领域逻辑)Orderorder=Order.create(CustomerId.of(command

月新闻