Back to Prompt Optimizer

开发经验总结

docs/archives/117-import-export-architecture-refactor/experience.md

2.10.26.9 KB
Original Source

开发经验总结

🎯 核心经验

大型架构重构的系统性方法

1. 问题识别要深入根因

经验: 表面问题往往指向更深层的架构问题

  • 现象: 数据导出只有4个设置项而不是8个
  • 表面原因: PreferenceService返回数据不完整
  • 根本原因: 存储键的双重用途设计不清晰
  • 架构问题: 集中式DataManager承担过多职责

最佳实践:

  • 不要急于修复表面问题
  • 深入分析问题的系统性原因
  • 考虑是否需要架构层面的改进

2. 接口优先的设计原则

经验: 先设计接口,再实现具体功能

typescript
// 先定义清晰的接口
export interface IImportExportable {
  exportData(): Promise<any>;
  importData(data: any): Promise<void>;
  getDataType(): Promise<string>;
  validateData(data: any): Promise<boolean>;
}

收益:

  • 强制思考职责边界
  • 便于并行开发
  • 提高代码可测试性
  • 支持依赖注入

3. 渐进式重构策略

经验: 大型重构要分阶段进行,保持功能连续性

实施步骤:

  1. 接口定义 - 创建新接口,避免循环依赖
  2. 服务改造 - 逐个服务实现新接口
  3. 协调层重构 - 最后修改DataManager
  4. 测试验证 - 每个阶段都要有测试覆盖

关键原则:

  • 保持现有API接口不变
  • 新旧系统并存一段时间
  • 充分测试后再移除旧代码

🛠️ 技术实现经验

存储键架构设计

双重用途的清晰分离

问题: 存储键既用于物理存储又用于JSON导出,容易混淆

解决方案:

typescript
// 物理存储键(带前缀)
'pref:app:settings:ui:theme-id'

// 逻辑JSON键(无前缀)  
'app:settings:ui:theme-id'

设计原则:

  • 在服务内部处理前缀转换
  • 对外暴露统一的逻辑键名
  • 文档化两种用途的映射关系

统一存储键管理

经验: 消除重复定义,建立单一数据源

  • 将storage-keys.ts从UI包移动到Core包
  • 所有模块引用同一个定义文件
  • 避免魔法字符串分散在代码中

Electron IPC架构

序列化问题处理

问题: Vue reactive对象无法通过IPC传输

解决方案:

typescript
// 在proxy层进行深度序列化
async exportData(): Promise<any> {
  const result = await window.electronAPI.service.exportData();
  return JSON.parse(JSON.stringify(result));
}

最佳实践:

  • 在IPC边界进行数据序列化
  • 使用TypeScript类型确保数据结构正确
  • 考虑大数据量的性能影响

代理层设计模式

经验: 代理类应该只负责IPC通信,不实现业务逻辑

typescript
// ✅ 正确:只负责转发
async getDataType(): Promise<string> {
  return await window.electronAPI.service.getDataType();
}

// ❌ 错误:在代理层实现逻辑
async getDataType(): Promise<string> {
  return 'hardcoded-value'; // 应该调用IPC
}

测试策略

分层测试体系

单元测试: 每个服务的导入导出功能 集成测试: 多服务协调工作 端到端测试: MCP浏览器自动化测试

AI自动化测试框架

创新点: 使用MCP工具进行浏览器自动化测试

  • 模拟真实用户操作
  • 验证UI交互和数据流
  • 可重复执行的测试用例

价值:

  • 快速发现回归问题
  • 验证架构一致性
  • 提高测试覆盖率

🚫 避坑指南

架构设计陷阱

1. 过度集中化

陷阱: 让一个类承担过多职责 表现: DataManager既协调又实现具体逻辑 后果: 代码难以维护,扩展困难

避免方法:

  • 遵循单一职责原则
  • 使用接口分离关注点
  • 定期重构过大的类

2. 接口设计不一致

陷阱: 不同服务使用不同的方法签名 表现: 有些返回Promise,有些同步返回 后果: 调用方需要特殊处理每个服务

避免方法:

  • 统一接口设计
  • 使用TypeScript强制类型检查
  • 代码审查时关注接口一致性

3. 存储抽象泄漏

陷阱: 存储层的实现细节暴露到业务层 表现: 业务代码需要知道存储键前缀 后果: 存储层变更影响业务逻辑

避免方法:

  • 在服务层封装存储细节
  • 使用逻辑键名对外暴露
  • 建立清晰的抽象边界

重构过程陷阱

1. 破坏性变更

陷阱: 修改现有API接口 后果: 影响现有调用代码,引入回归问题

避免方法:

  • 保持现有接口签名不变
  • 内部重构,外部兼容
  • 充分的回归测试

2. 测试覆盖不足

陷阱: 重构时没有足够的测试保护 后果: 引入难以发现的bug

避免方法:

  • 重构前先补充测试
  • 每个阶段都要有测试验证
  • 使用多层次测试策略

3. 文档滞后

陷阱: 代码重构了但文档没有更新 后果: 团队成员理解不一致,维护困难

避免方法:

  • 重构的同时更新文档
  • 创建架构决策记录(ADR)
  • 定期审查文档的准确性

🔄 架构设计经验

分布式服务架构

设计原则

  1. 单一职责: 每个服务只负责自己的数据
  2. 接口统一: 所有服务实现相同接口
  3. 松耦合: 服务间通过接口交互,不直接依赖
  4. 可扩展: 新增服务只需实现接口

实施要点

  • 先设计接口,再实现服务
  • 使用依赖注入管理服务关系
  • 建立统一的错误处理机制
  • 提供完整的测试覆盖

数据一致性保证

导入导出的原子性

挑战: 多个服务的数据需要保持一致性 解决方案:

  • 先验证所有数据格式
  • 再执行实际的导入操作
  • 出错时提供回滚机制

版本兼容性

设计: 在JSON中包含版本信息

json
{
  "version": 1,
  "exportTime": "2025-01-09T12:00:00.000Z",
  "data": { ... }
}

价值: 支持未来的数据格式升级

性能优化经验

减少不必要的数据传输

  • 在服务层进行数据过滤
  • 避免在协调层聚合大量数据
  • 使用流式处理处理大文件

并发处理

  • 各服务的导出可以并行执行
  • 使用Promise.all提高效率
  • 注意IPC调用的并发限制

💡 创新点总结

AI自动化测试框架

创新: 使用MCP工具进行端到端测试 价值: 验证真实用户场景,提高测试可靠性

存储键双重用途设计

创新: 明确分离物理存储键和逻辑JSON键 价值: 解决架构不一致问题,提高系统清晰度

分布式导入导出架构

创新: 从集中式改为分布式服务自管理 价值: 提高代码可维护性和扩展性

🔮 未来改进方向

架构演进

  • 考虑实现统一的缓存层
  • 支持增量导入导出
  • 添加数据压缩和加密

开发体验

  • 建立更完善的类型系统
  • 提供开发者工具支持
  • 增强错误诊断能力

测试自动化

  • 扩展AI测试框架覆盖更多场景
  • 建立性能回归测试
  • 实现持续集成中的自动化测试