DDD遗留系统迁移
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;
}
}
迁移检查清单
- 领域模型识别:明确各模块的领域边界
- 数据迁移方案:制定增量迁移和回滚策略
- 兼容性保证:新旧系统并行期的接口兼容
- 监控告警:迁移过程的监控和异常告警
- 灰度发布:小流量验证后再全量切换
遗留系统迁移是一个渐进过程,需要在业务连续性和技术改进之间找到平衡。