mirror of
https://github.com/linshenkx/prompt-optimizer.git
synced 2026-06-01 04:00:16 +08:00
## 核心架构重构 - 创建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接口,存储键架构发生变化
283 lines
8.5 KiB
Markdown
283 lines
8.5 KiB
Markdown
# 导入导出接口设计重构
|
||
|
||
## 📋 重构背景
|
||
|
||
用户提出了一个非常重要的架构观点:**"当前由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. **向后兼容原则** - 保护现有调用代码,避免破坏性更新
|
||
|
||
用户的建议非常准确,不仅指出了架构问题,还及时发现了兼容性问题,让系统更加稳定和可维护。
|