首页
/ 架构决策记录 (ADR)

架构决策记录 (ADR)

2026-04-03 09:43:54作者:廉皓灿Ida

标题:[使用Utility Process处理PDF渲染任务]

状态

[已接受] - [2023-11-15]

背景

应用需要生成复杂的PDF报表,当前在主线程处理导致UI卡顿超过2秒,影响用户体验。

决策

将PDF渲染任务迁移到独立的Utility Process中执行,通过IPC与主线程通信。

后果

  • 优点:UI响应性提升,渲染任务不会阻塞主线程
  • 缺点:增加了进程间通信复杂性,需要处理任务取消和错误恢复
  • 验证方法:测量PDF生成时的UI帧率,确保保持在60fps以上

## 三、实践架构重构:从理论到落地的实施

### 痛点分析:重构过程中的常见障碍

架构重构常面临三大挑战:**业务中断风险**、**技术债务累积**、**团队抵触情绪**。某项目在重构过程中因没有制定增量计划,试图"大爆炸"式重写,导致6周无产出,最终被迫回滚。另一团队因缺乏明确的重构目标,将重构异化为"代码美化",浪费大量精力却未解决核心架构问题。

成功的架构重构需要**明确的目标**、**增量的策略**和**可验证的效果**,而非追求理想中的"完美架构"。

### 实施路径:架构重构五步法

#### 1. 建立基准线

在重构前,通过**性能测试**和**代码分析**建立量化基准:
- 记录关键路径响应时间
- 统计模块依赖关系
- 评估代码质量指标

#### 2. 设计重构策略

根据架构诊断结果,制定三种可能的重构策略:

**增量式重构**(最推荐):
```java
// 逐步引入新架构,保持系统可运行
public class OrderService {
    // 旧实现
    private final LegacyOrderRepository legacyRepo;
    // 新实现
    private final NewOrderRepository newRepo;
    // 切换开关
    private final FeatureToggle useNewRepository;
    
    public Order findById(OrderId id) {
        if (useNewRepository.isEnabled()) {
            // 新实现路径
            return newRepo.findById(id)
                .orElseThrow(() -> new OrderNotFoundException(id));
        } else {
            // 旧实现路径
            return legacyRepo.getOrder(id);
        }
    }
}

分支式重构:适用于小型模块完全重写 并行运行式重构:适用于关键业务系统,新老系统并行运行验证

3. 实施模块解耦

以"依赖方向反转"原则重构模块间关系:

# 重构前:高层模块依赖低层模块
class OrderProcessor:
    def __init__(self):
        self.db = MySQLDatabase()  # 直接依赖具体实现
    
    def save_order(self, order):
        self.db.execute("INSERT INTO orders ...", order.to_dict())

# 重构后:依赖抽象接口
from abc import ABC, abstractmethod

class OrderRepository(ABC):
    @abstractmethod
    def save(self, order):
        pass

class MySQLOrderRepository(OrderRepository):
    def save(self, order):
        # 具体实现
        pass

class OrderProcessor:
    def __init__(self, repository: OrderRepository):
        self.repository = repository  # 依赖抽象
    
    def save_order(self, order):
        self.repository.save(order)

4. 建立自动化防护

为重构后的架构添加自动化测试质量门禁

// 架构守护测试:确保模块边界不被违反
@Test
void testModuleDependencies() {
    // 验证订单模块不依赖UI模块
    ArchitectureRule rule = Architectures.layeredArchitecture()
        .layer("Domain").definedBy("..domain..")
        .layer("UI").definedBy("..ui..")
        .whereLayer("Domain").mayNotBeAccessedByAnyLayer()
        .whereLayer("UI").mayOnlyAccessLayers("Domain");
        
    rule.check(importedClasses);
}

5. 持续优化迭代

建立架构适应性评估机制,定期检查并调整架构:

  • 每季度进行架构评审
  • 监控关键指标变化趋势
  • 建立架构技术债跟踪系统

效果验证:重构效果量化评估

通过上述方法,我们对一个中型Electron应用进行了为期3个月的架构重构,取得以下成果:

  • 构建时间:从45分钟减少至12分钟(-73%)
  • 测试覆盖率:从62%提升至89%
  • 模块复用率:从12%提升至41%
  • 变更影响范围:平均从8个文件减少至2.3个

重构后的应用界面

工具推荐

  • 重构工具:IntelliJ IDEA(代码重构支持)
  • 测试框架:JUnit 5 + Mockito(Java)、pytest(Python)
  • 质量门禁:Jenkins + SonarQube
  • 重构检查清单:安全重构实践指南

四、识别架构反模式:规避常见陷阱

痛点分析:架构设计的隐形杀手

即使经验丰富的架构师也可能陷入架构反模式的陷阱。某团队为追求"完美设计",引入了过多的设计模式,导致一个简单的CRUD应用包含15个抽象层,开发效率低下。另一项目因过度强调"复用",将不相关功能强行塞进同一模块,造成"上帝对象"(承担过多职责的类),最终不得不重写。

这些反模式往往在项目初期不易察觉,一旦形成便难以纠正,成为长期的技术债务。

实施路径:五种常见反模式及规避方案

1. 紧耦合架构

症状:修改一个模块导致多个不相关模块失效,模块间存在大量直接依赖。

规避方案:引入依赖注入接口抽象

# 反模式示例
class UserService:
    def __init__(self):
        self.db = PostgreSQLDatabase()  # 紧耦合具体数据库
        self.cache = RedisCache()       # 紧耦合具体缓存
    
    def get_user(self, user_id):
        # 直接操作具体实现
        user = self.cache.get(f"user:{user_id}")
        if not user:
            user = self.db.query("SELECT * FROM users WHERE id = %s", user_id)
            self.cache.set(f"user:{user_id}", user, 3600)
        return user

# 改进方案
class UserService:
    def __init__(self, repository, cache):
        self.repository = repository  # 依赖抽象
        self.cache = cache            # 依赖抽象
    
    def get_user(self, user_id):
        user = self.cache.get(f"user:{user_id}")
        if not user:
            user = self.repository.get(user_id)
            self.cache.set(f"user:{user_id}", user, 3600)
        return user

# 使用方式
service = UserService(PostgreSQLUserRepository(), RedisCache())

2. 分布式单体

症状:虽然部署为多个服务,但服务间存在同步调用链和共享数据库,本质仍是单体应用。

规避方案:按业务能力而非技术功能拆分服务,实现数据自治:

// 反模式:分布式单体中的订单服务
@Service
public class OrderService {
    @Autowired
    private RestTemplate restTemplate;
    
    public OrderDTO createOrder(OrderRequest request) {
        // 同步调用多个服务,形成脆弱的调用链
        UserDTO user = restTemplate.getForObject(
            "http://user-service/users/" + request.getUserId(), UserDTO.class);
            
        ProductDTO product = restTemplate.getForObject(
            "http://product-service/products/" + request.getProductId(), ProductDTO.class);
            
        // ... 其他服务调用 ...
        
        return orderRepository.save(new Order(user, product));
    }
}

3. 过度设计

症状:引入超出需求的复杂设计,如为简单应用添加事件溯源、CQRS等架构模式。

规避方案:遵循YAGNI原则(You Aren't Gonna Need It),采用演进式设计:

// 反模式:过度设计的日志系统
public interface Logger {
    void debug(String message);
    void info(String message);
    void warn(String message);
    void error(String message);
    // ... 20+ 其他方法 ...
}

public class LoggerFactory {
    public static Logger getLogger(Class<?> clazz) {
        // 复杂的日志实现选择逻辑
        if (System.getProperty("log.impl").equals("json")) {
            return new JsonLogger(clazz);
        } else if (System.getProperty("log.impl").equals("xml")) {
            return new XMLLogger(clazz);
        } else {
            return new ConsoleLogger(clazz);
        }
    }
}

// 改进方案:从简单实现开始
public class Logger {
    private final String className;
    
    public Logger(Class<?> clazz) {
        this.className = clazz.getSimpleName();
    }
    
    public void info(String message) {
        System.out.printf("[INFO] %s: %s%n", className, message);
    }
    
    // 只实现当前需要的方法
}

4. 数据孤岛

症状:各模块独立维护数据,导致数据不一致和重复存储。

规避方案:设计领域数据模型,明确数据所有权:

# 反模式:数据孤岛
# 用户模块
class User:
    def __init__(self, id, name, email, address):
        self.id = id
        self.name = name
        self.email = email
        self.address = address  # 重复存储地址信息

# 订单模块
class Order:
    def __init__(self, id, user_id, user_name, user_address, items):
        self.id = id
        self.user_id = user_id
        self.user_name = user_name  # 重复存储
        self.user_address = user_address  # 重复存储
        self.items = items

# 改进方案:明确数据所有权
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

class Address:
    def __init__(self, user_id, street, city, country):
        self.user_id = user_id
        self.street = street
        self.city = city
        self.country = country

class Order:
    def __init__(self, id, user_id, items, shipping_address_id):
        self.id = id
        self.user_id = user_id
        self.items = items
        self.shipping_address_id = shipping_address_id  # 引用而非复制

5. 硬编码配置

症状:环境配置、业务规则等硬编码在代码中,导致部署和维护困难。

规避方案:采用配置外部化规则引擎

// 反模式:硬编码配置
public class PaymentProcessor {
    // 硬编码的API密钥和URL
    private static final String API_KEY = "sk_test_1234567890";
    private static final String API_URL = "https://api.payment-provider.com/v1";
    
    public PaymentResult processPayment(BigDecimal amount) {
        // 硬编码的业务规则
        if (amount.compareTo(new BigDecimal("10000")) > 0) {
            throw new InvalidAmountException("单笔支付不能超过10000元");
        }
        
        // ... 处理支付 ...
    }
}

// 改进方案:配置外部化
@Configuration
@ConfigurationProperties(prefix = "payment")
public class PaymentConfig {
    private String apiKey;
    private String apiUrl;
    private BigDecimal maxAmount;
    
    // getters and setters
}

@Service
public class PaymentProcessor {
    private final PaymentConfig config;
    
    public PaymentProcessor(PaymentConfig config) {
        this.config = config;
    }
    
    public PaymentResult processPayment(BigDecimal amount) {
        if (amount.compareTo(config.getMaxAmount()) > 0) {
            throw new InvalidAmountException(
                String.format("单笔支付不能超过%s元", config.getMaxAmount()));
        }
        
        // 使用配置的API密钥和URL
        // ... 处理支付 ...
    }
}

效果验证:反模式识别与重构效果

通过反模式识别和重构,我们帮助一个电商平台解决了长期存在的架构问题:

  • 消除了3个主要循环依赖
  • 拆分了2个超过5000行的"上帝类"
  • 将硬编码配置减少了87%
  • 系统部署时间从4小时缩短至30分钟

架构重构前后对比

工具推荐

  • 反模式检测:PMD(Java代码分析器)、Bandit(Python安全分析器)
  • 配置管理:Spring Cloud Config、etcd
  • 规则引擎:Drools、Easy Rules
  • 反模式参考:架构反模式识别手册

五、扩展思考:架构演进的持续之道

痛点分析:架构僵化的风险

许多架构在设计完成后便进入"冻结"状态,无法适应业务和技术的变化。某企业核心系统因架构僵化,无法快速支持移动应用接入,错失市场机遇。另一项目因过度依赖特定技术栈,在面临性能瓶颈时难以切换到更适合的技术方案。

架构不是一次性的设计活动,而是持续演进的过程。成功的架构能够在保持稳定性的同时,支持必要的变化。

实施路径:构建可演进架构的四大支柱

1. 架构适应性设计

采用微内核架构,将核心功能与扩展功能分离:

// 微内核架构示例
public interface Plugin {
    String getName();
    void initialize(Kernel kernel);
    void shutdown();
}

public class Kernel {
    private final List<Plugin> plugins = new ArrayList<>();
    private final ServiceRegistry serviceRegistry = new ServiceRegistry();
    
    public void loadPlugin(Plugin plugin) {
        plugin.initialize(this);
        plugins.add(plugin);
    }
    
    public void registerService(String serviceName, Object service) {
        serviceRegistry.register(serviceName, service);
    }
    
    public <T> T getService(String serviceName, Class<T> serviceType) {
        return serviceType.cast(serviceRegistry.get(serviceName));
    }
    
    public void start() {
        // 启动核心服务
        // ...
    }
}

// 插件实现
public class PaymentPlugin implements Plugin {
    @Override
    public void initialize(Kernel kernel) {
        kernel.registerService("payment", new PaymentService());
    }
    
    // ...
}

2. 技术债务管理

建立技术债务跟踪系统,量化评估并定期偿还技术债务:

class TechnicalDebtItem:
    def __init__(self, id, title, module, severity, impact, effort):
        self.id = id
        self.title = title
        self.module = module
        self.severity = severity  # 1-5分
        self.impact = impact      # 对业务的影响
        self.effort = effort      # 修复所需人天
        self.status = "OPEN"
        
    def get_risk_score(self):
        # 计算风险分数:严重度 × 影响
        return self.severity * self.impact
        
    def get_cost_benefit_ratio(self):
        # 成本效益比:影响 / 修复成本
        return self.impact / self.effort

# 技术债务跟踪
class DebtTracker:
    def __init__(self):
        self.debts = []
        
    def add_debt(self, debt):
        self.debts.append(debt)
        
    def get_priority_debts(self, limit=5):
        # 按风险分数排序,返回优先级最高的债务
        return sorted(
            self.debts, 
            key=lambda x: x.get_risk_score(), 
            reverse=True
        )[:limit]
登录后查看全文
热门项目推荐
相关项目推荐