docs/archives/117-import-export-architecture-refactor/experience.md
经验: 表面问题往往指向更深层的架构问题
最佳实践:
经验: 先设计接口,再实现具体功能
// 先定义清晰的接口
export interface IImportExportable {
exportData(): Promise<any>;
importData(data: any): Promise<void>;
getDataType(): Promise<string>;
validateData(data: any): Promise<boolean>;
}
收益:
经验: 大型重构要分阶段进行,保持功能连续性
实施步骤:
关键原则:
问题: 存储键既用于物理存储又用于JSON导出,容易混淆
解决方案:
// 物理存储键(带前缀)
'pref:app:settings:ui:theme-id'
// 逻辑JSON键(无前缀)
'app:settings:ui:theme-id'
设计原则:
经验: 消除重复定义,建立单一数据源
问题: Vue reactive对象无法通过IPC传输
解决方案:
// 在proxy层进行深度序列化
async exportData(): Promise<any> {
const result = await window.electronAPI.service.exportData();
return JSON.parse(JSON.stringify(result));
}
最佳实践:
经验: 代理类应该只负责IPC通信,不实现业务逻辑
// ✅ 正确:只负责转发
async getDataType(): Promise<string> {
return await window.electronAPI.service.getDataType();
}
// ❌ 错误:在代理层实现逻辑
async getDataType(): Promise<string> {
return 'hardcoded-value'; // 应该调用IPC
}
单元测试: 每个服务的导入导出功能 集成测试: 多服务协调工作 端到端测试: MCP浏览器自动化测试
创新点: 使用MCP工具进行浏览器自动化测试
价值:
陷阱: 让一个类承担过多职责 表现: DataManager既协调又实现具体逻辑 后果: 代码难以维护,扩展困难
避免方法:
陷阱: 不同服务使用不同的方法签名 表现: 有些返回Promise,有些同步返回 后果: 调用方需要特殊处理每个服务
避免方法:
陷阱: 存储层的实现细节暴露到业务层 表现: 业务代码需要知道存储键前缀 后果: 存储层变更影响业务逻辑
避免方法:
陷阱: 修改现有API接口 后果: 影响现有调用代码,引入回归问题
避免方法:
陷阱: 重构时没有足够的测试保护 后果: 引入难以发现的bug
避免方法:
陷阱: 代码重构了但文档没有更新 后果: 团队成员理解不一致,维护困难
避免方法:
挑战: 多个服务的数据需要保持一致性 解决方案:
设计: 在JSON中包含版本信息
{
"version": 1,
"exportTime": "2025-01-09T12:00:00.000Z",
"data": { ... }
}
价值: 支持未来的数据格式升级
创新: 使用MCP工具进行端到端测试 价值: 验证真实用户场景,提高测试可靠性
创新: 明确分离物理存储键和逻辑JSON键 价值: 解决架构不一致问题,提高系统清晰度
创新: 从集中式改为分布式服务自管理 价值: 提高代码可维护性和扩展性