27 KiB
Part 5: 架构重构实践
本文档详细记录 Ops 模块架构重构的完整实践过程,为未来的架构演进提供参考。
5.1 问题识别
在阶段2和阶段3的实施过程中,我们发现了一些架构问题,需要进行重构。
5.1.1 DispatchEngine 职责不清晰
问题表现:
DispatchEngine 接口定义了 13 个方法,但很多方法返回 null 或空实现:
public interface DispatchEngine {
AssigneeRecommendation recommendAssignee(DispatchContext context);
// 以下方法职责不清晰
void autoDispatch(Long orderId); // 实际只是调用 orderQueueService.startExecution()
void handleOrderCompleted(Long orderId); // 业务逻辑,不应该在引擎中
void notifyAssignee(Long assigneeId); // 设备通知,不应该在引擎中
// 还有 10 个类似的方法...
}
问题分析:
- 职责边界模糊:派单引擎混合了决策、业务逻辑、设备通知等多种职责
- 命名误导:
autoDispatch看起来是自动派单,实际只是更新队列状态 - 扩展困难:新增业务类型时,需要修改 DispatchEngine 核心代码
影响:
- 开发人员困惑:不知道哪些功能应该放在 DispatchEngine 中
- 测试困难:职责过多导致单元测试复杂
- 维护成本高:修改一处可能影响多个功能
5.1.2 通用方法抽离不彻底
问题表现:
OpsOrderService 只有基础的 CRUD 操作,暂停/恢复等通用功能散落在各处:
// OpsOrderService 中只有基础操作
public interface OpsOrderService {
Long createOrder(OrderCreateReqDTO reqDTO);
void updateOrder(OrderUpdateReqDTO reqDTO);
void deleteOrder(Long orderId);
OpsOrderRespDTO getOrder(Long orderId);
}
// 暂停/恢复逻辑直接在 CleanOrderService 中实现
@Service
public class CleanOrderServiceImpl {
public void pauseOrder(Long orderId) {
// 1. 更新工单状态
orderStateMachine.transition(order, PAUSED, ...);
// 2. 更新队列状态 - 手动保证同步
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
orderQueueService.pauseTask(queue.getId());
// 问题:状态同步逻辑需要业务层手动处理,容易遗漏
}
}
问题分析:
- 重复代码:每个业务类型都要实现一遍暂停/恢复逻辑
- 一致性风险:手动同步工单状态和队列状态,容易出现不一致
- 缺少统一入口:没有统一的生命周期管理
影响:
- 代码重复率高
- 容易出现状态不一致的 bug
- 新增业务类型时需要重复实现
5.1.3 Listener 设计不明确
问题表现:
监听器注册是全局的,无法区分业务类型,且存在循环依赖风险:
@Component
public class CleanOrderStateChangeListener implements OrderStateChangeListener {
@Resource
private CleanOrderService cleanOrderService; // 依赖业务服务
@Override
public void onStateChanged(OrderStateChangedEvent event) {
// 问题1:无法区分业务类型,需要手动判断
if (!"CLEAN".equals(event.getOrderType())) {
return;
}
// 问题2:直接调用业务服务,可能产生循环依赖
cleanOrderService.handleStateChanged(event);
}
}
// CleanOrderService 同时依赖 DispatchEngine
@Service
public class CleanOrderServiceImpl {
@Resource
private DispatchEngine dispatchEngine; // 可能产生循环依赖
}
问题分析:
- 监听器注册全局化:所有监听器都会收到所有事件,需要手动过滤
- 缺少优先级定义:多个监听器执行顺序不确定
- 循环依赖风险:Listener → Service → Engine → Listener
影响:
- 性能浪费(全局广播)
- 循环依赖导致启动失败
- 难以控制执行顺序
5.1.4 状态与队列状态同步问题
问题表现:
工单状态(WorkOrderStatusEnum)和队列状态(OrderQueueStatusEnum)是两个独立的状态机:
// 工单状态
PENDING → ASSIGNED → ARRIVED → PAUSED → COMPLETED
// 队列状态
WAITING → PROCESSING → PAUSED → REMOVED
同步困难:
// 暂停工单时,需要同时更新两个状态
public void pauseOrder(Long orderId) {
// 步骤1:更新工单状态
orderStateMachine.transition(order, WorkOrderStatusEnum.PAUSED, ...);
// 步骤2:查询队列
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
// 步骤3:更新队列状态
orderQueueService.updateStatus(queue.getId(), OrderQueueStatusEnum.PAUSED);
// 问题:如果步骤3失败,会导致状态不一致
}
问题分析:
- 双写一致性:需要手动保证两个状态的同步
- 事务边界不清晰:状态更新可能跨越多个服务
- 缺少统一协调:没有统一的地方管理状态同步
影响:
- 容易出现状态不一致
- 调试困难
- 数据修复成本高
5.2 重构方案设计
5.2.1 核心设计原则
1. 单一职责原则(SRP):
- 每个组件只负责一个明确的职责
- DispatchEngine 只负责决策
- OrderStateMachine 只负责状态管理
- OrderLifecycleManager 负责状态同步
2. 开闭原则(OCP):
- 通过接口和策略模式支持扩展
- 新增业务类型不需要修改核心代码
3. 依赖倒置原则(DIP):
- 业务层依赖通用层的抽象接口
- 通用层不依赖具体业务实现
4. 清晰分层:
- 通用能力下沉(状态机、队列、派单引擎)
- 业务能力上浮(业务规则、事件处理)
5.2.2 组件职责重新定义
DispatchEngine(派单引擎)- 纯决策层:
public interface DispatchEngine {
/**
* 推荐执行人员(核心职责)
*/
AssigneeRecommendation recommendAssignee(DispatchContext context);
/**
* 判断是否可以打断当前任务
*/
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
/**
* 注册派单策略
*/
void registerStrategy(String businessType, AssignStrategy strategy);
}
职责:
- ✅ 根据上下文推荐最合适的执行人员
- ✅ 评估是否可以打断当前任务
- ✅ 管理派单策略
- ❌ 不负责状态管理
- ❌ 不负责设备通知
- ❌ 不负责业务逻辑
OrderLifecycleManager(生命周期管理)- 新增组件:
public interface OrderLifecycleManager {
/**
* 暂停工单(同步更新工单状态和队列状态)
*/
void pauseOrder(Long orderId, Long operatorId, String reason);
/**
* 恢复工单
*/
void resumeOrder(Long orderId, Long operatorId);
/**
* 打断工单(P0紧急任务场景)
*/
void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId);
/**
* 完成工单
*/
void completeOrder(Long orderId, Long operatorId);
}
职责:
- ✅ 统一管理工单状态和队列状态的同步变更
- ✅ 保证状态一致性(事务)
- ✅ 发布状态变更事件
- ❌ 不包含业务特定逻辑
OrderStateMachine(状态机)- 优化:
public interface OrderStateMachine {
/**
* 执行状态转换(不触发业务逻辑)
*/
TransitionResult transition(OpsOrderDO order,
WorkOrderStatusEnum newStatus,
OperatorTypeEnum operatorType,
Long operatorId,
String remark);
/**
* 检查状态转换是否合法
*/
boolean canTransition(WorkOrderStatusEnum from, WorkOrderStatusEnum to);
}
职责:
- ✅ 验证状态转换规则
- ✅ 记录状态变更事件
- ❌ 不再包含监听器机制(移除)
- ❌ 不触发任何业务逻辑
OrderEventPublisher(事件发布器)- 新增组件:
public interface OrderEventPublisher {
/**
* 发布状态变更事件
*/
void publishStateChanged(OrderStateChangedEvent event);
/**
* 发布工单创建事件
*/
void publishOrderCreated(OrderCreatedEvent event);
}
职责:
- ✅ 发布工单相关的领域事件
- ✅ 实现业务解耦
5.2.3 新架构设计图
重构前:
┌─────────────────────────────────────┐
│ DispatchEngine │
│ (职责混乱,13个方法) │
│ - 推荐人员 │
│ - 自动派单 │
│ - 设备通知 │
│ - 业务逻辑 │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ OrderStateMachine │
│ (包含监听器,触发业务逻辑) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ CleanOrderService │
│ (手动同步状态,容易出错) │
└─────────────────────────────────────┘
重构后:
┌──────────────────┐ ┌──────────────────┐
│ DispatchEngine │ │ OrderLifecycle │
│ (纯决策层) │ │ Manager │
│ - 推荐执行人 │ │ (状态同步器) │
│ - 评估打断 │ │ - 暂停/恢复 │
└────────┬─────────┘ │ - 打断/完成 │
│ └────────┬─────────┘
│ │
▼ ▼
┌────────────────────────────────────┐
│ OrderStateMachine │
│ (纯状态管理,不触发业务逻辑) │
└────────┬───────────────────────────┘
│
▼
┌────────────────────────────────────┐
│ OrderEventPublisher │
│ (事件发布器) │
└────────┬───────────────────────────┘
│
▼
┌────────────────────────────────────┐
│ CleanOrderEventHandler │
│ (保洁业务事件处理器) │
└────────────────────────────────────┘
5.2.4 重构前后对比
| 方面 | 重构前 | 重构后 |
|---|---|---|
| 职责划分 | 模糊,混合多种职责 | 清晰,单一职责 |
| 状态同步 | 业务层手动同步 | OrderLifecycleManager 统一管理 |
| 业务扩展 | 修改核心代码 | 实现事件处理器 |
| 循环依赖 | 存在风险 | 通过事件解耦 |
| 测试难度 | 困难,职责过多 | 容易,职责单一 |
5.3 重构实施过程
5.3.1 Phase 1: 基础重构
任务 1:重构 DispatchEngine 接口
Before:
public interface DispatchEngine {
AssigneeRecommendation recommendAssignee(DispatchContext context);
void autoDispatch(Long orderId);
void handleOrderCompleted(Long orderId);
void notifyAssignee(Long assigneeId);
// ... 还有 9 个方法
}
After:
public interface DispatchEngine {
/**
* 推荐执行人员(核心方法)
*/
AssigneeRecommendation recommendAssignee(DispatchContext context);
/**
* 评估是否可以打断
*/
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
/**
* 注册派单策略
*/
void registerStrategy(String businessType, AssignStrategy strategy);
}
任务 2:创建 OrderLifecycleManager
@Service
@Slf4j
public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
@Resource
private OrderStateMachine orderStateMachine;
@Resource
private OrderQueueService orderQueueService;
@Resource
private OrderEventPublisher eventPublisher;
@Override
@Transactional(rollbackFor = Exception.class)
public void pauseOrder(Long orderId, Long operatorId, String reason) {
// 1. 查询工单和队列
OpsOrderDO order = getOrderByOrderId(orderId);
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
// 2. 验证状态
validateCanPause(order, queueDTO);
// 3. 同步转换状态(事务保证原子性)
orderStateMachine.transition(order, PAUSED, operatorType, operatorId, reason);
if (queueDTO != null) {
orderQueueService.pauseTask(queueDTO.getId());
}
// 4. 发布事件
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
.orderId(orderId)
.orderType(order.getOrderType())
.oldStatus(ARRIVED)
.newStatus(PAUSED)
.operatorId(operatorId)
.build());
}
// ... 其他方法实现
}
任务 3:重构 OrderStateMachine(移除监听器)
Before:
@Service
public class OrderStateMachine {
private List<OrderStateChangeListener> listeners = new ArrayList<>();
public void registerListener(OrderStateChangeListener listener) {
listeners.add(listener);
}
@Transactional
public void transition(OpsOrderDO order, WorkOrderStatusEnum newStatus, ...) {
// 更新状态
order.setStatus(newStatus.name());
orderMapper.updateById(order);
// 触发监听器
for (OrderStateChangeListener listener : listeners) {
listener.onStateChanged(new OrderStateChangedEvent(...));
}
}
}
After:
@Component
public class OrderStateMachine {
@Resource
private OrderEventPublisher eventPublisher; // 改为使用事件发布器
@Transactional
public void transition(OpsOrderDO order, WorkOrderStatusEnum newStatus, ...) {
// 1. 验证状态转换
validateTransition(currentStatus, newStatus);
// 2. 更新状态
order.setStatus(newStatus.name());
orderMapper.updateById(order);
// 3. 记录事件
eventService.recordEvent(orderId, oldStatus, newStatus, ...);
// 4. 发布事件(替代监听器)
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
.orderId(order.getId())
.oldStatus(oldStatus)
.newStatus(newStatus)
.build());
}
}
任务 4:创建 OrderEventPublisher
@Service
@Slf4j
public class OrderEventPublisherImpl implements OrderEventPublisher {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@Override
@Async // 异步发布,不阻塞主流程
public void publishStateChanged(OrderStateChangedEvent event) {
try {
applicationEventPublisher.publishEvent(event);
log.info("状态变更事件已发布: orderId={}, {} -> {}",
event.getOrderId(), event.getOldStatus(), event.getNewStatus());
} catch (Exception e) {
log.error("状态变更事件发布失败: orderId={}", event.getOrderId(), e);
}
}
}
5.3.2 Phase 2: 业务层改造
任务 1:创建事件处理器替代监听器
Before(Listener):
@Component
public class CleanOrderStateChangeListener implements OrderStateChangeListener {
@Resource
private CleanOrderService cleanOrderService;
@Override
public void onStateChanged(OrderStateChangedEvent event) {
if (!"CLEAN".equals(event.getOrderType())) {
return;
}
cleanOrderService.handleStateChanged(event);
}
}
After(EventHandler):
@Component
public class CleanOrderEventHandler {
@Resource
private CleanerStatusService cleanerStatusService;
@Resource
private BadgeNotificationService badgeNotificationService;
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
// 只处理保洁类型的工单
if (!"CLEAN".equals(event.getOrderType())) {
return;
}
switch (event.getNewStatus()) {
case ARRIVED:
handleArrived(event);
break;
case PAUSED:
handlePaused(event);
break;
case COMPLETED:
handleCompleted(event);
break;
}
}
private void handleArrived(OrderStateChangedEvent event) {
// 更新保洁员状态为 BUSY
cleanerStatusService.updateStatus(event.getOperatorId(), CleanerStatusEnum.BUSY);
// 发送工牌通知
badgeNotificationService.notifyWorkStart(event.getOperatorId(), event.getOrderId());
}
private void handleCompleted(OrderStateChangedEvent event) {
// 更新保洁员状态为 IDLE
cleanerStatusService.updateStatus(event.getOperatorId(), CleanerStatusEnum.IDLE);
// 自动派发下一个任务
dispatchEngine.autoDispatchNext(event.getOrderId(), event.getOperatorId());
}
}
任务 2:重构 CleanOrderService
Before:
@Service
public class CleanOrderServiceImpl {
public void pauseOrder(Long orderId) {
// 手动同步状态
orderStateMachine.transition(order, PAUSED, ...);
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
orderQueueService.pauseTask(queue.getId());
}
}
After:
@Service
public class CleanOrderServiceImpl {
@Resource
private OrderLifecycleManager orderLifecycleManager; // 使用生命周期管理器
public void pauseOrder(Long orderId, String reason) {
// 委托给生命周期管理器,保证状态同步
orderLifecycleManager.pauseOrder(orderId, SecurityUtils.getLoginUserId(), reason);
// 业务特定逻辑
updateCleanerStatus(orderId, CleanerStatusEnum.PAUSED);
}
}
5.3.3 Phase 3: 优化与验证
任务 1:性能优化
优化点 1:事件异步发布
@Service
public class OrderEventPublisherImpl {
@Async("eventExecutor") // 使用独立线程池
public void publishStateChanged(OrderStateChangedEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
@Configuration
public class AsyncConfig {
@Bean("eventExecutor")
public Executor eventExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("event-");
executor.initialize();
return executor;
}
}
优化点 2:批量状态更新
public void batchPauseOrders(List<Long> orderIds, String reason) {
// 批量更新,减少数据库交互
orderLifecycleManager.batchPause(orderIds, operatorId, reason);
}
任务 2:集成测试
@SpringBootTest
class OrderLifecycleManagerIntegrationTest {
@Resource
private OrderLifecycleManager orderLifecycleManager;
@Resource
private OpsOrderMapper orderMapper;
@Resource
private OpsOrderQueueMapper queueMapper;
@Test
void testPauseOrder_ShouldSyncBothStatuses() {
// Given
Long orderId = createTestOrder();
// When
orderLifecycleManager.pauseOrder(orderId, 1L, "测试暂停");
// Then
OpsOrderDO order = orderMapper.selectById(orderId);
assertEquals("PAUSED", order.getStatus());
OpsOrderQueueDO queue = queueMapper.selectByOpsOrderId(orderId);
assertEquals("PAUSED", queue.getQueueStatus());
}
}
任务 3:文档更新
更新以下文档:
- part2-架构演进史.md - 添加重构历程
- part3-核心架构设计.md - 更新服务职责
- part5-架构重构实践.md - 本文档
- API 文档 - 更新接口说明
5.4 重构收获与反思
5.4.1 成功经验
1. 职责分离带来清晰度
重构后,每个组件的职责非常明确:
- DispatchEngine:只做决策,不管执行
- OrderLifecycleManager:只管状态同步,不管业务逻辑
- OrderEventPublisher:只管事件发布,不管事件处理
- EventHandler:只处理特定业务的事件
收益:
- 新人快速理解代码
- 单元测试简单明确
- 问题定位更快
2. 事件驱动架构提升扩展性
通过事件驱动,新增业务类型只需:
// 1. 实现事件处理器
@Component
public class RepairOrderEventHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
if (!"REPAIR".equals(event.getOrderType())) {
return;
}
// 处理维修工单的特定逻辑
}
}
// 2. 无需修改核心引擎代码
收益:
- 核心引擎稳定,无需修改
- 业务团队独立开发
- 新功能上线快
3. 兼容性保证平滑迁移
保留旧实现作为兼容层:
@Deprecated
public class OldDispatchEngine {
// 保留旧接口,内部委托给新实现
public void autoDispatch(Long orderId) {
orderLifecycleManager.dispatch(orderId);
}
}
收益:
- 业务流程不中断
- 逐步迁移,风险可控
- 回滚方案简单
5.4.2 踩坑记录
坑 1:循环依赖问题
问题:
@Service
public class DispatchEngineImpl {
@Resource
private CleanOrderService cleanOrderService; // DispatchEngine -> CleanOrderService
}
@Service
public class CleanOrderServiceImpl {
@Resource
private DispatchEngine dispatchEngine; // CleanOrderService -> DispatchEngine
}
// 启动报错:Circular dependency detected
解决方案:
@Service
public class DispatchEngineImpl {
@Lazy // 使用延迟注入
@Resource
private CleanOrderService cleanOrderService;
}
更好的解决方案:
// 通过事件解耦,避免直接依赖
@Component
public class CleanOrderEventHandler {
@EventListener
public void onOrderDispatched(OrderDispatchedEvent event) {
// 处理派单事件
}
}
坑 2:状态同步的事务边界
问题:
@Transactional
public void pauseOrder(Long orderId) {
// 更新工单状态(在事务内)
orderStateMachine.transition(order, PAUSED, ...);
// 更新队列状态(调用另一个服务,可能在另一个事务)
orderQueueService.pauseTask(queueId); // 如果失败,工单状态已提交
}
解决方案:
@Transactional(rollbackFor = Exception.class) // 确保所有异常都回滚
public void pauseOrder(Long orderId) {
// 所有数据库操作在同一个事务中
orderStateMachine.transition(order, PAUSED, ...);
orderQueueService.pauseTask(queueId);
// 事件发布在事务提交后(使用 @TransactionalEventListener)
eventPublisher.publishStateChanged(...);
}
坑 3:事件处理器的执行顺序
问题:
@Component
public class CleanOrderEventHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
// 处理器A:更新保洁员状态
}
}
@Component
public class CleanOrderNotificationHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
// 处理器B:发送通知
// 问题:可能在处理器A之前执行
}
}
解决方案:
@Component
public class CleanOrderEventHandler {
@EventListener
@Order(1) // 优先级1,先执行
public void onStateChanged(OrderStateChangedEvent event) {
// 更新保洁员状态
}
}
@Component
public class CleanOrderNotificationHandler {
@EventListener
@Order(2) // 优先级2,后执行
public void onStateChanged(OrderStateChangedEvent event) {
// 发送通知
}
}
5.4.3 改进建议
1. 从设计阶段就明确职责
在编码前:
- 使用 UML 类图展示组件关系
- 定义清晰的接口契约
- 明确依赖方向
2. 持续重构,不要积累技术债务
建立机制:
- 每季度进行架构 Review
- 及时重构不合理的设计
- 不要等到问题严重才重构
3. 完善测试,保证重构质量
测试策略:
- 单元测试覆盖核心逻辑
- 集成测试覆盖端到端流程
- 重构前后对比测试
5.5 兼容性处理
5.5.1 保证平滑迁移
兼容层设计:
@Deprecated
@Service("oldDispatchEngine")
public class DispatchEngineAdapter implements OldDispatchEngine {
@Resource
private DispatchEngine newDispatchEngine;
@Resource
private OrderLifecycleManager orderLifecycleManager;
@Override
public void autoDispatch(Long orderId) {
// 委托给新实现
orderLifecycleManager.dispatch(orderId);
}
@Override
public void notifyAssignee(Long assigneeId) {
// 废弃方法,记录警告
log.warn("notifyAssignee() is deprecated, please use EventHandler instead");
}
}
5.5.2 逐步切换策略
阶段 1:保留旧接口
- 新旧实现并存
- 旧接口内部调用新实现
- 添加 @Deprecated 注解
阶段 2:迁移业务代码
- 逐个模块迁移到新接口
- 测试验证功能正常
阶段 3:清理旧代码
- 确认所有业务已迁移
- 删除旧接口和兼容层
5.5.3 版本兼容性
版本规划:
- v2.0: 新架构发布,旧接口标记为 Deprecated
- v2.1: 旧接口保留,持续迁移
- v3.0: 删除旧接口
下一章:Part 6: 性能与可靠性