Files
aiot-platform-cloud/docs/ops-architecture/part5-架构重构实践.md

27 KiB
Raw Blame History

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 个类似的方法...
}

问题分析

  1. 职责边界模糊:派单引擎混合了决策、业务逻辑、设备通知等多种职责
  2. 命名误导autoDispatch 看起来是自动派单,实际只是更新队列状态
  3. 扩展困难:新增业务类型时,需要修改 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());

        // 问题:状态同步逻辑需要业务层手动处理,容易遗漏
    }
}

问题分析

  1. 重复代码:每个业务类型都要实现一遍暂停/恢复逻辑
  2. 一致性风险:手动同步工单状态和队列状态,容易出现不一致
  3. 缺少统一入口:没有统一的生命周期管理

影响

  • 代码重复率高
  • 容易出现状态不一致的 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;  // 可能产生循环依赖
}

问题分析

  1. 监听器注册全局化:所有监听器都会收到所有事件,需要手动过滤
  2. 缺少优先级定义:多个监听器执行顺序不确定
  3. 循环依赖风险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失败会导致状态不一致
}

问题分析

  1. 双写一致性:需要手动保证两个状态的同步
  2. 事务边界不清晰:状态更新可能跨越多个服务
  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创建事件处理器替代监听器

BeforeListener

@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);
    }
}

AfterEventHandler

@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: 性能与可靠性