# AuroraDesk 项目重构计划 ## 📋 项目概述 **目标项目名**: AuroraDesk **原项目名**: MyAvaloniaApp **目标架构**: 整洁架构 (Clean Architecture) **重点优化**: 减少 ViewModels 耦合,提高代码可维护性 **环境**: Windows 10 **注意事项**: 避免使用 Linux 语法,防止乱码问题 --- ## 🎯 重构目标 ### 1. 项目重命名 - 项目名称:`MyAvaloniaApp` → `AuroraDesk` - 解决方案文件:`MyAvaloniaApp.sln` → `AuroraDesk.sln` - 项目文件:`MyAvaloniaApp.csproj` → `AuroraDesk.csproj` - 命名空间前缀:`MyAvaloniaApp.*` → `AuroraDesk.*` ### 2. 架构重构(整洁架构) #### 2.1 整洁架构依赖关系规则 整洁架构的核心原则是 **依赖关系必须指向内层**,即依赖方向永远向内(朝向核心层/Domain层)。 **关键要点**: - **Core(Domain)层定义所有接口**,不是 Application 层 - **Application 和 Infrastructure 都依赖 Core**,但两者之间不互相依赖 - **Infrastructure 实现 Core 层定义的接口** ##### 📌 基本依赖规则 ``` ┌─────────────────────────────────────────────┐ │ Presentation Layer (表示层) │ │ - ViewModels, Views, Converters │ │ ↓ 依赖 ↓ │ ├─────────────────────────────────────────────┤ │ Application Layer (应用层) │ │ - Use Cases, DTOs, Application Logic │ │ ↓ 依赖 ↓ │ ├─────────────────────────────────────────────┤ │ Core/Domain Layer (核心领域层) │ │ - Entities, Domain Interfaces │ │ - 定义所有服务接口 │ │ (不依赖任何外部层) │ └─────────────────────────────────────────────┘ ↑ 依赖 ↑ ↑ 实现 ↑ ┌─────────────────────────────────────────────┐ │ Infrastructure Layer (基础设施层) │ │ - Service Implementations, Data Access │ │ - 实现 Core 层定义的接口 │ │ (不依赖 Application 层) │ └─────────────────────────────────────────────┘ ``` ##### ✅ 正确的依赖关系 1. **Core(核心领域层 / Domain)** - ✅ **无依赖**:不依赖任何其他层 - ✅ **被依赖**:所有层都可以依赖 Core - ✅ **包含**: - 领域实体(Entities) - **领域接口(如 `INavigationService`, `IDataService` 等)** - 领域异常(Exceptions) 2. **Application(应用层)** - ✅ **依赖**:只依赖 Core 层 - ✅ **不依赖**:不依赖 Infrastructure 层 - ✅ **被依赖**:Presentation 层可以依赖 Application - ✅ **包含**: - 用例逻辑(Use Cases) - DTOs(数据传输对象) - 应用服务逻辑(使用 Core 接口) 3. **Infrastructure(基础设施层)** - ✅ **依赖**:只依赖 Core 层 - ✅ **不依赖**:不依赖 Application 层 - ✅ **实现**:实现 Core 层定义的接口 - ✅ **包含**: - 服务实现(如 `NavigationService`, `DataService`) - 数据访问实现 - 外部 API 调用 4. **Presentation(表示层)** - ✅ **依赖**:依赖 Core 和 Application 层 - ✅ **不依赖**:不直接依赖 Infrastructure 层(通过接口) - ✅ **包含**:ViewModels、Views、UI 相关组件 ##### ❌ 错误的依赖关系(禁止) ``` ❌ Core → Application // 核心层不能依赖应用层 ❌ Core → Infrastructure // 核心层不能依赖基础设施层 ❌ Core → Presentation // 核心层不能依赖表示层 ❌ Application → Infrastructure // 应用层不能依赖基础设施层 ❌ Infrastructure → Application // 基础设施层不能依赖应用层(关键!) ❌ Application → Presentation // 应用层不能依赖表示层 ❌ Presentation → Infrastructure // 表示层不能直接依赖基础设施层(通过接口) ``` ##### 🔄 依赖倒置原则(DIP) **关键要点**: - **Core 层定义接口**(如 `INavigationService`),不是 Application 层 - **Infrastructure 层实现 Core 层定义的接口**(如 `NavigationService`) - **Application 层使用 Core 层的接口**,但不依赖 Infrastructure - **Presentation 层通过接口依赖服务**,不依赖具体实现 **正确的示例**: ```csharp // ✅ 正确:Core 层定义接口 namespace AuroraDesk.Core.Interfaces { public interface INavigationService { void Navigate(string pageId); ObservableCollection GetNavigationItems(); } public interface IDataService { } public interface IApiService { } } // ✅ 正确:Infrastructure 层实现 Core 接口 namespace AuroraDesk.Infrastructure.Services { public class NavigationService : INavigationService { // 实现接口方法 } } // ✅ 正确:Application 层使用 Core 接口(不定义接口) namespace AuroraDesk.Application.Services { public class NavigationUseCase { private readonly INavigationService _navigationService; // 使用 Core 接口 public NavigationUseCase(INavigationService navigationService) { _navigationService = navigationService; } } } // ✅ 正确:Presentation 层依赖 Core 接口和 Application 用例 namespace AuroraDesk.Presentation.ViewModels { public class MainWindowViewModel { private readonly INavigationService _navigationService; // 依赖 Core 接口 private readonly NavigationUseCase _useCase; // 依赖 Application 用例 } } ``` **错误的示例**: ```csharp // ❌ 错误:Application 层不应该定义接口 namespace AuroraDesk.Application.Services { public interface INavigationService { } // 应该在 Core 层! } // ❌ 错误:Infrastructure 不应该依赖 Application namespace AuroraDesk.Infrastructure.Services { using AuroraDesk.Application; // 错误!不能依赖 Application public class NavigationService : INavigationService { } } ``` ##### 📊 本项目依赖关系图(修正后) ``` AuroraDesk.Presentation (表示层) ├─→ 依赖 AuroraDesk.Application (应用用例) └─→ 依赖 AuroraDesk.Core (核心实体和接口) AuroraDesk.Infrastructure (基础设施层) ├─→ 依赖 AuroraDesk.Core (核心实体和接口) └─→ 实现 AuroraDesk.Core.Interfaces 的接口 ❌ 不依赖 AuroraDesk.Application AuroraDesk.Application (应用层) └─→ 依赖 AuroraDesk.Core (核心实体和接口) ❌ 不依赖 AuroraDesk.Infrastructure AuroraDesk.Core (核心层/Domain) └─→ 无依赖(独立层) └─→ 定义所有接口 ``` ##### 🎯 依赖检查清单(修正后) 在重构过程中,确保: - [ ] Core 层没有任何 using 语句引用其他层 - [ ] **Core 层定义所有服务接口**(INavigationService, IDataService 等) - [ ] Application 层只引用 Core 层 - [ ] **Application 层不定义接口,只使用 Core 接口** - [ ] Infrastructure 层只引用 Core 层 - [ ] **Infrastructure 层不依赖 Application 层** - [ ] Infrastructure 层实现 Core 层定义的接口 - [ ] Presentation 层通过接口依赖服务,不直接依赖 Infrastructure - [ ] 所有跨层访问都通过接口进行 - [ ] 没有循环依赖 ### 3. 解耦 ViewModels 策略 #### 3.1 问题分析 **当前问题**: - `MainWindowViewModel` 中直接创建了所有 `PageViewModel` 实例(高耦合) - ViewModel 创建逻辑分散在 `InitializeNavigationItems` 方法中 - 难以进行单元测试 - 违反单一职责原则 **解决方案**: 1. **引入 PageViewModel 工厂模式** - 创建 `IPageViewModelFactory` 接口 - 实现 `PageViewModelFactory` 类 - 通过依赖注入管理 ViewModel 创建 2. **引入导航服务** - 创建 `INavigationService` 接口 - 封装导航逻辑,减少 MainWindowViewModel 职责 - 统一管理导航项配置 3. **将配置数据外置** - 创建导航配置数据类(NavigationConfig) - 从 ViewModel 中分离配置数据 - 配置可在应用启动时注入 #### 3.2 重构后的依赖关系 ``` MainWindowViewModel ↓ (依赖) INavigationService // 处理导航逻辑 IPageViewModelFactory // 创建 PageViewModel ILogger // 日志记录 IScreen // ReactiveUI 路由 不再直接依赖: ❌ DashboardPageViewModel ❌ UsersPageViewModel ❌ SettingsPageViewModel ... (所有具体的 PageViewModel) ``` --- ## 📝 详细重构步骤 ### 阶段一:准备工作(不修改代码) #### 步骤 1.1: 创建新项目结构(可选,建议先单项目重构) - [ ] 创建分层项目结构(或多文件夹结构) - [ ] 规划文件迁移路径 - [ ] 备份当前代码 #### 步骤 1.2: 分析依赖关系 - [ ] 梳理所有 ViewModel 之间的依赖 - [ ] 识别所有直接 new 实例化的地方 - [ ] 记录需要注入的服务 --- ### 阶段二:架构重构(按顺序执行) #### 步骤 2.1: 创建核心领域层 - [ ] 创建 `AuroraDesk.Core` 项目/文件夹 - [ ] 迁移实体类:`NavigationItem`, `TabItem` 到 `Core.Entities` - [ ] 更新命名空间:`AuroraDesk.Core.Entities` - [ ] 移除业务逻辑,只保留数据模型 #### 步骤 2.2: 创建核心层接口和应用层 - [ ] 在 `AuroraDesk.Core.Interfaces` 中定义所有服务接口 - [ ] `INavigationService` 接口(不是 Application 层!) - [ ] `IDataService` 接口 - [ ] `IApiService` 接口 - [ ] `IResourceService` 接口 - [ ] `IPageViewModelFactory` 接口 - [ ] 创建 `AuroraDesk.Application` 项目/文件夹 - [ ] 定义用例类(使用 Core 接口) - [ ] 定义导航配置 DTO(NavigationConfig) #### 步骤 2.3: 创建基础设施层实现 - [ ] 创建 `AuroraDesk.Infrastructure` 项目/文件夹 - [ ] **重要**:Infrastructure 只依赖 Core 层,不依赖 Application 层 - [ ] 实现 Core 层定义的接口: - [ ] `NavigationService` 实现 `INavigationService` - [ ] `PageViewModelFactory` 实现 `IPageViewModelFactory` - [ ] `DataService` 实现 `IDataService` - [ ] `ApiService` 实现 `IApiService` - [ ] `ResourceService` 实现 `IResourceService` - [ ] 更新服务命名空间为 `AuroraDesk.Infrastructure.Services` #### 步骤 2.4: 重构表示层 - [ ] 更新 `MainWindowViewModel`,移除直接创建 ViewModel 的代码 - [ ] 注入 `INavigationService` 和 `IPageViewModelFactory` - [ ] 简化 `MainWindowViewModel` 的职责 - [ ] 更新所有命名空间为 `AuroraDesk.Presentation.*` --- ### 阶段三:项目重命名(谨慎执行) #### 步骤 3.1: 重命名项目文件 - [ ] 重命名 `.csproj` 文件 - [ ] 重命名 `.sln` 文件 - [ ] 更新项目文件中的 `` 和 `` #### 步骤 3.2: 重命名命名空间(逐个文件修改) - [ ] **不要使用批量替换** - [ ] 逐个文件修改命名空间声明 - [ ] 逐个文件更新 using 语句 - [ ] 每次修改后编译测试 #### 步骤 3.3: 更新配置文件 - [ ] 更新 `appsettings.json` 中的项目相关配置 - [ ] 更新 `app.manifest` 中的程序集名称 - [ ] 更新所有批处理文件和脚本 --- ### 阶段四:解耦 ViewModels(重点) #### 步骤 4.1: 在 Core 层定义接口 ```csharp // AuroraDesk.Core.Interfaces/IPageViewModelFactory.cs public interface IPageViewModelFactory { T CreatePageViewModel(IScreen screen) where T : IRoutableViewModel; IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen); } // AuroraDesk.Core.Interfaces/INavigationService.cs public interface INavigationService { ObservableCollection GetNavigationItems(); void NavigateToPage(NavigationItem item); } ``` #### 步骤 4.2: 在 Infrastructure 层实现接口 ```csharp // AuroraDesk.Infrastructure/Services/PageViewModelFactory.cs public class PageViewModelFactory : IPageViewModelFactory { private readonly IServiceProvider _serviceProvider; public IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen) { return pageId switch { "dashboard" => _serviceProvider.GetRequiredService(), "users" => _serviceProvider.GetRequiredService(), // ... 其他页面 _ => throw new ArgumentException($"Unknown page: {pageId}") }; } } // AuroraDesk.Infrastructure/Services/NavigationService.cs public class NavigationService : INavigationService { // 实现接口方法 } ``` #### 步骤 4.3: 在 Application 层创建用例(可选) ```csharp // AuroraDesk.Application/UseCases/NavigationUseCase.cs public class NavigationUseCase { private readonly INavigationService _navigationService; // 使用 Core 接口 public NavigationUseCase(INavigationService navigationService) { _navigationService = navigationService; } // 用例逻辑 } ``` #### 步骤 4.4: 重构 MainWindowViewModel - [ ] 移除 `InitializeNavigationItems` 方法中的 ViewModel 创建逻辑 - [ ] 注入 `INavigationService`(来自 Core.Interfaces)获取导航项 - [ ] 使用 `IPageViewModelFactory`(来自 Core.Interfaces)创建 ViewModel - [ ] 简化导航逻辑,委托给 `INavigationService` - [ ] **重要**:ViewModel 依赖 Core 接口,不直接依赖 Infrastructure --- ### 阶段五:注册依赖注入 #### 步骤 5.1: 更新 ServiceCollectionExtensions - [ ] 注册 `IPageViewModelFactory` → `PageViewModelFactory` - [ ] 注册 `INavigationService` → `NavigationService` - [ ] 注册所有 PageViewModel 为 Transient(或 Scoped) - [ ] 更新命名空间引用 #### 步骤 5.2: 更新 App.axaml.cs - [ ] 确保所有新服务正确注册 - [ ] 更新 ViewLocator 的命名空间 - [ ] 测试依赖注入是否正常工作 --- ### 阶段六:测试和验证 #### 步骤 6.1: 编译测试 - [ ] 清理解决方案并重新编译 - [ ] 修复所有编译错误 - [ ] 修复所有命名空间引用错误 #### 步骤 6.2: 运行时测试 - [ ] 启动应用程序 - [ ] 测试导航功能 - [ ] 测试所有页面能否正常加载 - [ ] 测试标签页功能 #### 步骤 6.3: 代码检查 - [ ] 检查是否还有直接 new ViewModel 的地方 - [ ] 检查命名空间是否全部更新 - [ ] 检查是否有循环依赖 --- ## 🔧 技术实现细节 ### 1. PageViewModel 注册方式 ```csharp // ServiceCollectionExtensions.cs services.AddTransient(); services.AddTransient(); services.AddTransient(); // ... 其他 PageViewModel ``` ### 2. 导航配置数据 ```csharp // NavigationConfig.cs public class NavigationConfig { public List Items { get; set; } = new(); } public class NavigationItemConfig { public string Id { get; set; } = string.Empty; public string Title { get; set; } = string.Empty; public string PageViewModelType { get; set; } = string.Empty; public IconType IconType { get; set; } public List? Children { get; set; } } ``` ### 3. MainWindowViewModel 重构示例 **重构前**: ```csharp private void InitializeNavigationItems() { _navigationItems = new ObservableCollection { new NavigationItem { ViewModel = new DashboardPageViewModel(_screen) }, new NavigationItem { ViewModel = new UsersPageViewModel(_screen) }, // ... }; } ``` **重构后**: ```csharp using AuroraDesk.Core.Interfaces; // 依赖 Core 接口,不依赖 Infrastructure private readonly INavigationService _navigationService; public MainWindowViewModel( IScreen screen, INavigationService navigationService, // Core 接口 ILogger? logger = null) { _screen = screen; _navigationService = navigationService; _logger = logger; NavigationItems = _navigationService.GetNavigationItems(); } ``` --- ## ⚠️ 注意事项 ### 1. 不要批量替换 - ❌ **禁止**:使用 Visual Studio 的全局查找替换功能批量替换命名空间 - ✅ **正确做法**:逐个文件修改,每次修改后编译测试 ### 2. Windows 10 环境 - ❌ **避免**:使用 Linux 风格的路径分隔符或命令 - ✅ **使用**:Windows 路径分隔符 `\` 或使用 `Path.Combine` - ✅ **使用**:PowerShell 脚本而非 Bash 脚本 ### 3. 编译顺序 - 先编译被依赖的层: - **Core**(最底层,无依赖) - **Application** 和 **Infrastructure**(都依赖 Core,可并行编译) - **Presentation**(依赖 Core 和 Application) - 每次修改一层后立即编译测试 ### 4. Git 版本控制 - 每个阶段完成后提交一次代码 - 使用有意义的提交信息 - 如果出现问题可以回退到上一个阶段 --- ## 📊 预期成果 ### 代码质量提升 - ✅ ViewModels 耦合度降低 80%+ - ✅ 代码可测试性提升 - ✅ 符合整洁架构原则 - ✅ 易于扩展新功能 ### 项目结构 - ✅ 清晰的分层架构 - ✅ 统一的命名空间规范(Aurora 前缀) - ✅ 更好的代码组织 ### 维护性 - ✅ 新页面添加只需注册,无需修改 MainWindowViewModel - ✅ 导航逻辑集中管理 - ✅ 配置与代码分离 --- ## 📅 执行时间估算 - **阶段一**(准备):0.5 天 - **阶段二**(架构重构):2-3 天 - **阶段三**(重命名):1-2 天 - **阶段四**(解耦):2-3 天 - **阶段五**(DI注册):0.5 天 - **阶段六**(测试):1 天 **总计**:约 7-10 个工作日 --- ## ✅ 检查清单 在每个阶段完成后,使用此清单验证: - [ ] 所有文件命名空间已更新 - [ ] 所有 using 语句已更新 - [ ] 项目文件已更新 - [ ] 解决方案文件已更新 - [ ] 代码编译无错误 - [ ] 应用程序可以正常启动 - [ ] 主要功能正常工作 - [ ] 已更新 modify.md 记录修改 --- ## 📝 修改记录 每次完成一个重要步骤后,请在 `modify.md` 文件中记录: - 修改日期 - 修改内容 - 修改的文件列表 - 遇到的问题及解决方案 --- **文档创建日期**: 2025年1月 **最后更新**: 2025年1月 **状态**: 计划阶段