我之前接手过一个订单系统,4 个开发一起写的,跑了 2 年。某天产品说"加个拼团功能",我看着代码结构,改了 23 个文件才把一个简单的"拼团订单"塞进去。

复盘时老板问我为什么这么慢。我指着代码说:"这不是订单系统,是一坨面条。"
整个订单系统里,没有聚合根、没有工厂、没有领域事件,全是 Service 层堆业务逻辑。Service 调 Service,方法套方法,500 行的 OrderService.createOrder() 是常事。
这种代码的根本问题不是"烂",是设计模式的彻底缺位。本文用一个真实的订单中心重构案例,讲清楚 DDD 聚合根 + 工厂模式怎么落地。
先看你大概率写过的代码:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private OrderItemRepository itemRepository;
@Autowired
private StockService stockService;
@Autowired
private CouponService couponService;
@Autowired
private PaymentService paymentService;
@Autowired
private UserService userService;
public Long createOrder(CreateOrderRequest req) {
// 1. 校验用户
User user = userService.getById(req.getUserId());
if (user == null) throw new RuntimeException("用户不存在");
// 2. 校验库存
for (OrderItemDTO item : req.getItems()) {
Stock stock = stockService.getBySkuId(item.getSkuId());
if (stock.getCount() < item.getCount()) {
throw new RuntimeException("库存不足");
}
}
// 3. 校验优惠券
if (req.getCouponId() != null) {
Coupon coupon = couponService.getById(req.getCouponId());
if (!coupon.isValid()) throw new RuntimeException("优惠券无效");
}
// 4. 算价格
BigDecimal totalPrice = BigDecimal.ZERO;
for (OrderItemDTO item : req.getItems()) {
Product product = productService.getById(item.getSkuId());
totalPrice = totalPrice.add(product.getPrice().multiply(BigDecimal.valueOf(item.getCount())));
}
if (req.getCouponId() != null) {
totalPrice = couponService.applyDiscount(req.getCouponId(), totalPrice);
}
// 5. 扣库存
for (OrderItemDTO item : req.getItems()) {
stockService.decrease(item.getSkuId(), item.getCount());
}
// 6. 创建订单
Order order = new Order();
order.setUserId(req.getUserId());
order.setStatus(OrderStatus.PENDING);
order.setTotalPrice(totalPrice);
orderRepository.save(order);
// 7. 创建订单项
for (OrderItemDTO item : req.getItems()) {
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setSkuId(item.getSkuId());
orderItem.setCount(item.getCount());
orderItem.setPrice(...);
itemRepository.save(orderItem);
}
// 8. 创建支付单
Payment payment = new Payment();
payment.setOrderId(order.getId());
payment.setAmount(totalPrice);
paymentService.create(payment);
return order.getId();
}
}
150 行业务逻辑,3 个 if,5 个 for,没有任何抽象。这就是典型的"贫血模型 + 事务脚本"反模式。
加个拼团功能?恭喜你,在 createOrder() 里再加 50 行 if 吧,订单类型再多几种就破 500 行了。这就是"一改就崩"的根因——所有逻辑堆在一个方法里,没有边界。
聚合根(Aggregate Root)是 DDD 里的核心概念:一组相关对象的"老板",外部只能通过它来访问内部对象。
拿订单来说,一个聚合根包含:
外部不能直接修改 OrderItem,必须通过 Order 提供的领域方法:
public class Order {
private Long id;
private Long userId;
private OrderStatus status;
private List<OrderItem> items;
private OrderAddress address;
private Money totalPrice;
private List<DomainEvent> events = new ArrayList<>();
// 工厂方法:创建订单
public static Order create(CreateOrderCommand cmd, PricingService pricingService) {
// 聚合根内部封装创建逻辑
Order order = new Order();
order.userId = cmd.getUserId();
order.status = OrderStatus.PENDING;
order.items = cmd.getItems().stream()
.map(item -> OrderItem.create(item))
.collect(Collectors.toList());
order.address = OrderAddress.create(cmd.getAddress());
order.totalPrice = pricingService.calculate(order.items);
order.events.add(new OrderCreatedEvent(order));
return order;
}
// 领域方法:添加订单项
public void addItem(OrderItem item) {
if (this.status != OrderStatus.PENDING) {
throw new DomainException("只能修改待支付订单");
}
this.items.add(item);
this.totalPrice = this.totalPrice.add(item.getSubtotal());
}
// 领域方法:支付
public void pay(Payment payment) {
if (this.status != OrderStatus.PENDING) {
throw new DomainException("订单状态异常: " + this.status);
}
if (!payment.getAmount().equals(this.totalPrice)) {
throw new DomainException("支付金额不符");
}
this.status = OrderStatus.PAID;
this.events.add(new OrderPaidEvent(this));
}
}
所有业务规则在 Order 内部校验,外部 Service 只负责协调(事务、事件发布、跨聚合调用)。
普通订单、拼团订单、预售订单——创建逻辑各不相同,但调用方不应该知道这些差异。这就是工厂模式的用武之地。
public interface OrderFactory {
Order create(CreateOrderCommand cmd);
boolean supports(OrderType type);
}@Component
public class NormalOrderFactory implements OrderFactory {
@Override
public boolean supports(OrderType type) {
return type == OrderType.NORMAL;
}
@Override
public Order create(CreateOrderCommand cmd) {
// 普通订单的创建逻辑
Order order = new Order();
// ...
return order;
}
}@Component
public class GroupBuyOrderFactory implements OrderFactory {
@Override
public boolean supports(OrderType type) {
return type == OrderType.GROUP_BUY;
}
@Override
public Order create(CreateOrderCommand cmd) {
// 拼团订单的创建逻辑
Order order = new GroupBuyOrder(); // 继承自 Order
order.setGroupId(cmd.getGroupId());
order.setExpireAt(LocalDateTime.now().plusHours(2));
// ...
return order;
}
}
调用方只看到一个统一的入口:
@Component
public class OrderFactoryRegistry {
private final List<OrderFactory> factories;
public OrderFactoryRegistry(List<OrderFactory> factories) {
this.factories = factories;
}
public Order createOrder(CreateOrderCommand cmd) {
return factories.stream()
.filter(f -> f.supports(cmd.getOrderType()))
.findFirst()
.orElseThrow(() -> new DomainException("不支持的订单类型: " + cmd.getOrderType()))
.create(cmd);
}
}
这是策略模式 + 工厂模式 + 依赖注入的组合应用。Spring 帮你把 List<OrderFactory> 注入进来,你不用手动注册。
加新功能时,最痛苦的是改一个地方要动 23 个文件。领域事件 + 观察者模式能彻底解决这个问题。
public abstract class AggregateRoot {
private List<DomainEvent> events = new ArrayList<>();
protected void raiseEvent(DomainEvent event) {
this.events.add(event);
}
public List<DomainEvent> getDomainEvents() {
return Collections.unmodifiableList(events);
}
public void clearEvents() {
this.events.clear();
}
}
聚合根在状态变化时发布事件:
public class Order extends AggregateRoot {
public void pay(Payment payment) {
// ... 业务校验
this.status = OrderStatus.PAID;
raiseEvent(new OrderPaidEvent(this.id, this.totalPrice)); // 发布事件
}
}
应用层捕获事件并分发:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ApplicationEventPublisher springPublisher;
@Transactional
public Long createOrder(CreateOrderCommand cmd) {
Order order = factoryRegistry.createOrder(cmd);
orderRepository.save(order);
// 提交事务后发布领域事件
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
order.getDomainEvents().forEach(springPublisher::publishEvent);
order.clearEvents();
}
}
);
return order.getId();
}
}
事件订阅方各做各的事:
@Component
public class StockEventHandler {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderPaidEvent event) {
// 扣库存
stockService.decrease(event.getOrderId());
}
}@Component
public class CouponEventHandler {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderPaidEvent event) {
// 标记优惠券已使用
couponService.markUsed(event.getOrderId());
}
}@Component
public class NotificationEventHandler {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderPaidEvent event) {
// 发通知
notificationService.sendOrderPaid(event.getOrderId());
}
}
加新功能只需要加一个 @Component 事件订阅者,Order 本身一行代码不用改。这就是观察者模式的威力。
| 维度 | 重构前 | 重构后 |
|---|---|---|
| 加拼团功能要改的文件 | 23 个 | 1 个(新工厂)+ 1 个(新事件订阅者) |
| 业务校验分散在 | 5 个 Service | Order 聚合根内部 |
| 单元测试 | 必须 mock 5 个 Service | 直接测 Order 领域方法 |
| 业务规则变更 | 全局搜索 if 改 if | 改 Order 一个方法 |
| 代码总行数 | 5000+ | 1800(含注释和测试) |
代码量减少了 60%,但加新功能的速度快了 3 倍。这就是 DDD + 工厂模式 + 观察者模式的真实价值。
把 Order、Payment、Refund、AfterSale 全塞进一个聚合根。每次下单要锁整个聚合,高并发下变成单线程。
正确做法:Order 聚合只包含创建订单必需的对象,Payment 是另一个聚合,Refund 是另一个聚合。它们之间通过领域事件解耦。
聚合根只有 getter/setter,没有业务方法。order.setStatus(PAID) 满天飞。
正确做法:所有状态变更走领域方法。order.pay(payment) 而不是 order.setStatus(PAID)。领域方法里做校验、记录事件、调用其他领域方法。
每个对象都套个工厂。UserFactory、ProductFactory、AddressFactory...
正确做法:只有创建逻辑复杂、多种变体、需要依赖注入的才用工厂。简单的对象 new 一个就行。工厂不是越多越好。
DDD 聚合根 + 工厂模式 + 领域事件不是"装 X 用的高级货",是解决"加功能要改 23 个文件"这个真实痛点的工程方案。
如果你正在维护一个"改不动"的老系统,试试从最大的 Service 抽一个聚合根开始重构。先让一个对象有"自己的行为",再谈模式。一个能落地的工厂,比 10 个写得像教科书的工厂强。
下次写订单系统之前先问自己:
答不上来就先别写——回去看看订单领域到底在干什么。