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

978 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Part 5: 架构重构实践
本文档详细记录 Ops 模块架构重构的完整实践过程,为未来的架构演进提供参考。
---
## 5.1 问题识别
在阶段2和阶段3的实施过程中我们发现了一些架构问题需要进行重构。
### 5.1.1 DispatchEngine 职责不清晰
**问题表现**
DispatchEngine 接口定义了 13 个方法,但很多方法返回 null 或空实现:
```java
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 操作,暂停/恢复等通用功能散落在各处:
```java
// 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 设计不明确
**问题表现**
监听器注册是全局的,无法区分业务类型,且存在循环依赖风险:
```java
@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`)是两个独立的状态机:
```java
// 工单状态
PENDING ASSIGNED ARRIVED PAUSED COMPLETED
// 队列状态
WAITING PROCESSING PAUSED REMOVED
```
**同步困难**
```java
// 暂停工单时,需要同时更新两个状态
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派单引擎- 纯决策层**
```java
public interface DispatchEngine {
/**
* 推荐执行人员(核心职责)
*/
AssigneeRecommendation recommendAssignee(DispatchContext context);
/**
* 判断是否可以打断当前任务
*/
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
/**
* 注册派单策略
*/
void registerStrategy(String businessType, AssignStrategy strategy);
}
```
**职责**
- ✅ 根据上下文推荐最合适的执行人员
- ✅ 评估是否可以打断当前任务
- ✅ 管理派单策略
- ❌ 不负责状态管理
- ❌ 不负责设备通知
- ❌ 不负责业务逻辑
**OrderLifecycleManager生命周期管理- 新增组件**
```java
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状态机- 优化**
```java
public interface OrderStateMachine {
/**
* 执行状态转换(不触发业务逻辑)
*/
TransitionResult transition(OpsOrderDO order,
WorkOrderStatusEnum newStatus,
OperatorTypeEnum operatorType,
Long operatorId,
String remark);
/**
* 检查状态转换是否合法
*/
boolean canTransition(WorkOrderStatusEnum from, WorkOrderStatusEnum to);
}
```
**职责**
- ✅ 验证状态转换规则
- ✅ 记录状态变更事件
- ❌ 不再包含监听器机制(移除)
- ❌ 不触发任何业务逻辑
**OrderEventPublisher事件发布器- 新增组件**
```java
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**
```java
public interface DispatchEngine {
AssigneeRecommendation recommendAssignee(DispatchContext context);
void autoDispatch(Long orderId);
void handleOrderCompleted(Long orderId);
void notifyAssignee(Long assigneeId);
// ... 还有 9 个方法
}
```
**After**
```java
public interface DispatchEngine {
/**
* 推荐执行人员(核心方法)
*/
AssigneeRecommendation recommendAssignee(DispatchContext context);
/**
* 评估是否可以打断
*/
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
/**
* 注册派单策略
*/
void registerStrategy(String businessType, AssignStrategy strategy);
}
```
**任务 2创建 OrderLifecycleManager**
```java
@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**
```java
@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**
```java
@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**
```java
@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**
```java
@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**
```java
@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**
```java
@Service
public class CleanOrderServiceImpl {
public void pauseOrder(Long orderId) {
// 手动同步状态
orderStateMachine.transition(order, PAUSED, ...);
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
orderQueueService.pauseTask(queue.getId());
}
}
```
**After**
```java
@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事件异步发布**
```java
@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批量状态更新**
```java
public void batchPauseOrders(List<Long> orderIds, String reason) {
// 批量更新,减少数据库交互
orderLifecycleManager.batchPause(orderIds, operatorId, reason);
}
```
**任务 2集成测试**
```java
@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文档更新**
更新以下文档:
- [x] part2-架构演进史.md - 添加重构历程
- [x] part3-核心架构设计.md - 更新服务职责
- [ ] part5-架构重构实践.md - 本文档
- [ ] API 文档 - 更新接口说明
---
## 5.4 重构收获与反思
### 5.4.1 成功经验
**1. 职责分离带来清晰度**
重构后,每个组件的职责非常明确:
- DispatchEngine只做决策不管执行
- OrderLifecycleManager只管状态同步不管业务逻辑
- OrderEventPublisher只管事件发布不管事件处理
- EventHandler只处理特定业务的事件
**收益**
- 新人快速理解代码
- 单元测试简单明确
- 问题定位更快
**2. 事件驱动架构提升扩展性**
通过事件驱动,新增业务类型只需:
```java
// 1. 实现事件处理器
@Component
public class RepairOrderEventHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
if (!"REPAIR".equals(event.getOrderType())) {
return;
}
// 处理维修工单的特定逻辑
}
}
// 2. 无需修改核心引擎代码
```
**收益**
- 核心引擎稳定,无需修改
- 业务团队独立开发
- 新功能上线快
**3. 兼容性保证平滑迁移**
保留旧实现作为兼容层:
```java
@Deprecated
public class OldDispatchEngine {
// 保留旧接口,内部委托给新实现
public void autoDispatch(Long orderId) {
orderLifecycleManager.dispatch(orderId);
}
}
```
**收益**
- 业务流程不中断
- 逐步迁移,风险可控
- 回滚方案简单
### 5.4.2 踩坑记录
**坑 1循环依赖问题**
**问题**
```java
@Service
public class DispatchEngineImpl {
@Resource
private CleanOrderService cleanOrderService; // DispatchEngine -> CleanOrderService
}
@Service
public class CleanOrderServiceImpl {
@Resource
private DispatchEngine dispatchEngine; // CleanOrderService -> DispatchEngine
}
// 启动报错Circular dependency detected
```
**解决方案**
```java
@Service
public class DispatchEngineImpl {
@Lazy // 使用延迟注入
@Resource
private CleanOrderService cleanOrderService;
}
```
**更好的解决方案**
```java
// 通过事件解耦,避免直接依赖
@Component
public class CleanOrderEventHandler {
@EventListener
public void onOrderDispatched(OrderDispatchedEvent event) {
// 处理派单事件
}
}
```
**坑 2状态同步的事务边界**
**问题**
```java
@Transactional
public void pauseOrder(Long orderId) {
// 更新工单状态(在事务内)
orderStateMachine.transition(order, PAUSED, ...);
// 更新队列状态(调用另一个服务,可能在另一个事务)
orderQueueService.pauseTask(queueId); // 如果失败,工单状态已提交
}
```
**解决方案**
```java
@Transactional(rollbackFor = Exception.class) // 确保所有异常都回滚
public void pauseOrder(Long orderId) {
// 所有数据库操作在同一个事务中
orderStateMachine.transition(order, PAUSED, ...);
orderQueueService.pauseTask(queueId);
// 事件发布在事务提交后(使用 @TransactionalEventListener
eventPublisher.publishStateChanged(...);
}
```
**坑 3事件处理器的执行顺序**
**问题**
```java
@Component
public class CleanOrderEventHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
// 处理器A更新保洁员状态
}
}
@Component
public class CleanOrderNotificationHandler {
@EventListener
public void onStateChanged(OrderStateChangedEvent event) {
// 处理器B发送通知
// 问题可能在处理器A之前执行
}
}
```
**解决方案**
```java
@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 保证平滑迁移
**兼容层设计**
```java
@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: 性能与可靠性](./part6-性能与可靠性.md)