← 返回首页
🎯

DDD遗留系统迁移

📂 architecture ⏱ 3 min 423 words

DDD遗留系统迁移

迁移策略选择

遗留系统迁移需要根据系统状况选择合适的策略。

迁移策略对比:

绞杀者模式(Strangler Fig Pattern)
┌─────────────────────────────────────────┐
│              API 网关                     │
│    ┌──────────────┬──────────────┐       │
│    │   新服务      │   遗留系统    │       │
│    │   (DDD)      │   (Monolith) │       │
│    └──────────────┴──────────────┘       │
│           ↑                  ↑            │
│     逐步替换            逐步缩小           │
└─────────────────────────────────────────┘

修缮者模式(Refactoring Pattern)
┌─────────────────────────────────────────┐
│           遗留系统(内部重构)              │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ 模块A   │  │ 模块B   │  │ 模块C   │  │
│  │ (已重构) │  │ (重构中) │  │ (待重构) │  │
│  └─────────┘  └─────────┘  └─────────┘  │
└─────────────────────────────────────────┘

绞杀者模式实现

// API 网关路由配置
@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            // 新服务:订单模块
            .route("order-service", r -> r
                .path("/api/orders/**")
                .filters(f -> f.stripPrefix(0))
                .uri("lb://order-service"))
            // 遗留系统:用户模块
            .route("legacy-user", r -> r
                .path("/api/users/**")
                .filters(f -> f.stripPrefix(0))
                .uri("lb://legacy-system"))
            // 遗留系统:报表模块
            .route("legacy-report", r -> r
                .path("/api/reports/**")
                .filters(f -> f.stripPrefix(0))
                .uri("lb://legacy-system"))
            .build();
    }
}

// 防腐层:隔离遗留系统模型
@Component
public class LegacyUserACL {
    
    private final LegacyUserClient legacyClient;
    
    public User toDomainUser(LegacyUserDto legacyDto) {
        return User.builder()
            .id(new UserId(legacyDto.getUSER_ID()))
            .username(new Username(legacyDto.getLOGIN_NAME()))
            .email(new Email(legacyDto.getEMAIL()))
            .build();
    }
    
    public LegacyUserDto toLegacyDto(CreateUserCommand command) {
        LegacyUserDto dto = new LegacyUserDto();
        dto.setLOGIN_NAME(command.getUsername().getValue());
        dto.setEMAIL(command.getEmail().getValue());
        return dto;
    }
    
    public User createUser(CreateUserCommand command) {
        LegacyUserDto legacyDto = toLegacyDto(command);
        LegacyUserDto created = legacyClient.createUser(legacyDto);
        return toDomainUser(created);
    }
}

修缮者模式实现

// 模块化重构:逐步提取领域模型
// Step 1: 识别订单模块边界
package com.example.monolith.order;

// 新的领域模型
public class Order {
    private OrderId id;
    private OrderStatus status;
    private List<OrderItem> items;
    
    public static Order create(Long customerId, List<OrderItem> items) {
        Order order = new Order();
        order.id = OrderId.generate();
        order.status = OrderStatus.CREATED;
        order.items = new ArrayList<>(items);
        return order;
    }
}

// Step 2: 适配层连接新旧代码
@Component
public class OrderAdapter {
    
    private final LegacyOrderRepository legacyRepo;
    private final NewOrderRepository newRepo;
    
    public Order getOrder(String orderId) {
        // 优先从新存储读取
        Optional<Order> newOrder = newRepo.findById(orderId);
        if (newOrder.isPresent()) {
            return newOrder.get();
        }
        
        // 回退到遗留存储
        LegacyOrder legacy = legacyRepo.findById(orderId);
        Order order = convertToNewModel(legacy);
        
        // 迁移数据到新存储
        newRepo.save(order);
        
        return order;
    }
    
    public void saveOrder(Order order) {
        // 同时写入新旧存储(双写期)
        newRepo.save(order);
        legacyRepo.save(convertToLegacyModel(order));
    }
}

数据迁移策略

// 增量数据迁移
@Component
public class DataMigrationService {
    
    private final LegacyDataSource legacySource;
    private final NewDataSource newTarget;
    
    @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨2点
    public void migrateIncremental() {
        LocalDateTime lastSync = getLastSyncTime();
        
        // 查询变更数据
        List<LegacyOrder> changedOrders = legacySource
            .findAfterModifiedAt(lastSync);
        
        for (LegacyOrder legacy : changedOrders) {
            try {
                Order newOrder = convertToNewModel(legacy);
                newTarget.saveOrUpdate(newOrder);
                
                log.info("迁移订单: {}", legacy.getId());
            } catch (Exception e) {
                log.error("迁移订单失败: {}", legacy.getId(), e);
                recordMigrationError(legacy.getId(), e.getMessage());
            }
        }
        
        updateLastSyncTime(LocalDateTime.now());
    }
}

// 双写期数据一致性保证
@Component
public class DualWriteService {
    
    @Transactional
    public Order createOrder(CreateOrderCommand command) {
        Order order = Order.create(command);
        
        // 写入新存储
        newOrderRepository.save(order);
        
        // 写入遗留存储(异步)
        asyncExecutor.execute(() -> {
            try {
                LegacyOrder legacy = convertToLegacyModel(order);
                legacyOrderRepository.save(legacy);
            } catch (Exception e) {
                // 记录失败,后续补偿
                compensationService.recordFailedWrite(order.getId());
            }
        });
        
        return order;
    }
}

迁移检查清单

遗留系统迁移是一个渐进过程,需要在业务连续性和技术改进之间找到平衡。