Files
prompt-optimizer/docs/architecture/import-export-interface-design.md
linshen 71600b5081 feat: 重构导入导出架构
## 核心架构重构
- 创建IImportExportable接口,定义统一的导入导出规范
- 新增ImportExportError错误类,提供专门的错误处理
- 将导入导出逻辑从集中式DataManager分散到各个服务
- DataManager职责精简:从375行减至67行(-308行),仅负责协调

## 存储键架构优化
- 移动storage-keys.ts从ui包到core包,实现统一管理
- 修复存储键双重用途问题:物理存储键vs逻辑JSON导出键
- PreferenceService添加'pref:'前缀处理物理存储
- 解决数据导出不完整的关键架构缺陷

## 服务层改造
### ModelManager
- 实现IImportExportable接口(+209行,-153行)
- 添加exportData/importData/validateData方法
- 保持向后兼容的数据格式

### TemplateManager
- 实现分布式导入导出逻辑
- 移除过度设计的configurable storageKey
- 统一使用PreferenceService管理用户偏好

### HistoryManager
- 添加完整的导入导出实现
- 支持数据验证和错误处理

### PreferenceService
- 实现统一的用户设置导入导出
- 处理存储键前缀转换逻辑
- 支持builtin-template-language等核心设置

## Electron桌面端更新
- main.js: 新增148行IPC处理逻辑
- preload.js: 新增177行API暴露
- 更新所有service proxy类支持新接口
- 保持IPC通信的类型安全

## 测试体系完善
- 新增各服务专门的import-export.test.ts文件
- 创建data/import-export-integration.test.ts集成测试
- 建立AI自动化测试框架验证存储键一致性
- 更新现有测试适配新架构

## 文档与架构说明
- 创建import-export-interface-design.md设计文档
- 添加storage-key-architecture.md架构说明
- 建立AI自动化测试文档体系
- 更新workspace文档记录重构过程

BREAKING CHANGE: 导入导出接口从集中式DataManager重构为分布式服务实现,
各服务现在必须实现IImportExportable接口,存储键架构发生变化
2025-07-12 11:56:14 +08:00

283 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 导入导出接口设计重构
## 📋 重构背景
用户提出了一个非常重要的架构观点:**"当前由DataManager来实现import和export的具体实现不合理。应该抽象一个接口定义有导入导出方法。让各个service类去继承如 IModelManager、IPreferenceService等。要求他们必须实现这个接口。DataManager只负责总体协调具体实现由各个类负责。"**
## 🎯 问题分析
### 当前架构的问题
1. **职责不清** - DataManager既要协调又要了解每个服务的具体实现细节
2. **耦合度高** - DataManager需要知道如何调用每个服务的具体方法
3. **扩展性差** - 新增服务需要修改DataManager的实现
4. **违反单一职责原则** - DataManager承担了太多责任
### 目标架构
1. **职责分离** - DataManager只负责协调各服务负责自己的导入导出
2. **接口统一** - 所有服务实现相同的导入导出接口
3. **扩展性好** - 新增服务只需实现接口无需修改DataManager
4. **遵循开闭原则** - 对扩展开放,对修改关闭
## 🔧 解决方案
### 1. 定义统一的导入导出接口
```typescript
/**
* 可导入导出的服务接口
* 所有需要参与数据导入导出的服务都应该实现此接口
*/
export interface IImportExportable {
/**
* 导出服务的所有数据
* @returns 服务数据的JSON表示
*/
exportData(): Promise<any>;
/**
* 导入数据到服务
* @param data 要导入的数据
* @returns 导入结果
*/
importData(data: any): Promise<ImportExportResult>;
/**
* 获取服务的数据类型标识
* 用于在导入导出JSON中标识数据类型
*/
getDataType(): string;
/**
* 验证数据格式是否正确
* @param data 要验证的数据
* @returns 是否为有效格式
*/
validateData(data: any): boolean;
}
```
### 2. 更新服务接口继承关系
```typescript
// 所有需要导入导出的服务都继承IImportExportable
export interface IModelManager extends IImportExportable { /* ... */ }
export interface IPreferenceService extends IImportExportable { /* ... */ }
export interface ITemplateManager extends IImportExportable { /* ... */ }
export interface IHistoryManager extends IImportExportable { /* ... */ }
```
### 3. 实现简洁的DataCoordinator简化后
```typescript
export class DataCoordinator implements IDataManager {
private readonly services: IImportExportable[];
// 直接通过构造函数注入所有服务,简单直接
constructor(services: IImportExportable[]) {
this.services = services;
}
/**
* 导出所有数据 - 只负责协调
*/
async exportAllData(): Promise<ExportData> {
const data: Record<string, any> = {};
// 并行导出所有服务的数据
const exportPromises = this.services.map(async (service) => {
const dataType = service.getDataType();
const serviceData = await service.exportData();
data[dataType] = serviceData;
});
await Promise.all(exportPromises);
return { version: 1, timestamp: Date.now(), data };
}
/**
* 导入所有数据 - 只负责协调
*/
async importAllData(exportData: ExportData): Promise<ImportExportResult> {
// 并行导入所有服务的数据
const importPromises = Object.entries(exportData.data).map(async ([dataType, serviceData]) => {
const service = this.services.find(s => s.getDataType() === dataType);
if (service) {
return await service.importData(serviceData);
}
});
const results = await Promise.all(importPromises);
// 汇总结果...
}
}
// 使用示例:简单的工厂函数
export function createDataCoordinator(services: IImportExportable[]): DataCoordinator {
return new DataCoordinator(services);
}
```
## 📊 架构对比
### 修改前DataManager承担所有职责
```typescript
// ❌ DataManager需要了解每个服务的具体实现
class DataManager {
async exportAllData() {
const userSettings = await this.preferenceService.getAll();
const models = await this.modelManager.getAllModels();
const templates = await this.templateManager.listTemplates();
const history = await this.historyManager.getAllRecords();
// DataManager需要知道每个服务的具体方法名和返回格式
}
}
```
### 修改后:简洁的协调者模式
```typescript
// ✅ DataCoordinator只负责协调不关心具体实现
class DataCoordinator {
constructor(services: IImportExportable[]) {
this.services = services; // 简单的依赖注入
}
async exportAllData() {
// 统一调用每个服务的exportData()方法
const exportPromises = this.services.map(async (service) => {
const dataType = service.getDataType();
data[dataType] = await service.exportData();
});
}
}
// 使用时直接传入所有服务
const coordinator = new DataCoordinator([
modelManager,
preferenceService,
templateManager,
historyManager
]);
```
## 🎯 实现细节
### 各服务的实现示例
#### ModelManager实现
```typescript
export class ModelManager implements IModelManager {
async exportData(): Promise<ModelConfig[]> {
return await this.getAllModels();
}
async importData(data: any): Promise<ImportExportResult> {
if (!this.validateData(data)) {
return { success: false, message: 'Invalid model data format' };
}
// 具体导入逻辑...
}
getDataType(): string {
return 'models';
}
validateData(data: any): boolean {
return Array.isArray(data) && data.every(/* 验证逻辑 */);
}
}
```
#### PreferenceService实现
```typescript
export class PreferenceService implements IPreferenceService {
async exportData(): Promise<Record<string, string>> {
return await this.getAll();
}
async importData(data: any): Promise<ImportExportResult> {
if (!this.validateData(data)) {
return { success: false, message: 'Invalid preference data format' };
}
// 具体导入逻辑...
}
getDataType(): string {
return 'userSettings';
}
validateData(data: any): boolean {
return typeof data === 'object' && /* 验证逻辑 */;
}
}
```
## 🚀 优势总结
### 1. 职责清晰
- **DataCoordinator**: 只负责协调各服务的导入导出
- **各服务**: 只负责自己数据的导入导出实现
- **接口**: 定义统一的行为规范
### 2. 扩展性强
- 新增服务只需实现`IImportExportable`接口
- 无需修改DataCoordinator的代码
- 支持动态注册和注销服务
### 3. 可测试性好
- 每个服务的导入导出逻辑可以独立测试
- DataCoordinator的协调逻辑可以用mock服务测试
- 接口定义明确,便于编写单元测试
### 4. 维护性高
- 各服务的导入导出逻辑内聚在服务内部
- 修改某个服务的导入导出逻辑不影响其他部分
- 代码结构清晰,便于理解和维护
## 📝 迁移计划
### 已完成
- [x] 定义`IImportExportable`接口
- [x] 更新所有服务接口继承关系
- [x] 实现ModelManager的导入导出接口
- [x] 实现PreferenceService的导入导出接口
- [x] 实现TemplateManager的导入导出接口
- [x] 创建DataCoordinator协调者类
### 待完成
- [ ] 实现HistoryManager的导入导出接口
- [ ] 更新应用初始化代码使用DataCoordinator
- [ ] 更新所有相关测试
- [ ] 废弃旧的DataManager类
## ⚠️ 重要修正:接口兼容性
### 破坏性更新问题
在重构过程中,我们差点引入了破坏性更新:
```typescript
// ❌ 原来的接口(破坏性更新)
async exportAllData(): Promise<ExportData>;
async importAllData(data: ExportData): Promise<ImportExportResult>;
// ✅ 修正后的接口(保持兼容)
async exportAllData(): Promise<string>;
async importAllData(dataString: string): Promise<ImportExportResult>;
```
### 兼容性原则
1. **保持现有接口签名** - 不改变方法参数和返回类型
2. **内部重构,外部不变** - 内部可以使用新的数据结构,但对外接口保持一致
3. **渐进式升级** - 如需变更先标记为deprecated再逐步迁移
## 🎉 总结
这次重构体现了优秀的架构设计原则:
1. **单一职责原则** - 每个类只负责一个职责
2. **开闭原则** - 对扩展开放,对修改关闭
3. **依赖倒置原则** - 依赖抽象而不是具体实现
4. **接口隔离原则** - 接口设计精简且职责明确
5. **向后兼容原则** - 保护现有调用代码,避免破坏性更新
用户的建议非常准确,不仅指出了架构问题,还及时发现了兼容性问题,让系统更加稳定和可维护。