You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

6.5 KiB

为什么 PageViewModelFactory 必须在 Presentation 层?

🎯 核心原因

PageViewModelFactory 直接依赖 Presentation 层的 ViewModel 类型!

📊 依赖关系分析

依赖链 1:PageViewModelFactory → ViewModels

AuroraDesk.Core.Interfaces.IPageViewModelFactory (接口定义)
          ↑
          | 实现
          |
AuroraDesk.Presentation.Services.PageViewModelFactory
          ↓ 依赖
    [必须知道具体的 ViewModel 类型]
    - DashboardPageViewModel
    - UsersPageViewModel
    - SettingsPageViewModel
    - ReportsPageViewModel
    - ... (所有 PageViewModel)

关键代码

// AuroraDesk.Presentation/Services/PageViewModelFactory.cs

// ❌ 这里必须引用具体的 ViewModel 类型!
using AuroraDesk.Presentation.ViewModels.Pages;  // ← 关键!
using AuroraDesk.Core.Interfaces;  // 接口定义

public class PageViewModelFactory : IPageViewModelFactory
{
    public IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen)
    {
        return pageId switch
        {
            "dashboard" => CreatePageViewModel<DashboardPageViewModel>(screen),  // ← 需要具体的 ViewModel 类型
            "users" => CreatePageViewModel<UsersPageViewModel>(screen),
            "settings" => CreatePageViewModel<SettingsPageViewModel>(screen),
            // ...
        };
    }
}

🚫 为什么不放在其他层?

方案1:放在 Infrastructure 层?

AuroraDesk.Infrastructure.Services.PageViewModelFactory
    ↓ 依赖
AuroraDesk.Presentation.ViewModels.Pages.*  // ❌ 违反依赖倒置!

问题

  • Infrastructure 不能依赖 Presentation
  • 违反了整洁架构的依赖规则
  • 会导致循环依赖

方案2:放在主项目(AuroraDesk)? ⚠️

AuroraDesk/Services/PageViewModelFactory.cs
    ↓ 依赖
AuroraDesk.Presentation.ViewModels.Pages.*  // ✅ 可以

优点

  • 主项目可以访问所有层
  • 符合依赖规则

缺点

  • 主项目会变得臃肿
  • 降低了分层清晰度
  • 当前架构中,主项目主要用于启动配置

方案3:放在 Presentation 层?

AuroraDesk.Presentation.Services.PageViewModelFactory
    ↓ 依赖
AuroraDesk.Presentation.ViewModels.Pages.*  // ✅ 同层,完全合理!

优点

  • 在同一层,无需跨层依赖
  • 职责清晰:Presentation 层管理所有 UI 相关组件
  • 符合依赖倒置:实现 Core 的接口,使用同一层的类型

📚 依赖倒置原则(DIP)解析

什么是依赖倒置?

高层模块不应该依赖低层模块,两者都应该依赖抽象。

在我们的架构中

高层(Presentation)                   低层(Infrastructure)
     ↓                                       ↓
使用接口(IPageViewModelFactory)    ←  实现接口(但在不同层)
     ↓                                       ↓
使用具体类型(DashboardPageViewModel)  ←  这是 Presentation 层内部的事

关键理解

  1. 接口在 Core 层定义(IPageViewModelFactory)
  2. 实现可以在 Presentation 层(PageViewModelFactory)
  3. 使用同层的具体类型(DashboardPageViewModel)

这并不违反依赖倒置,因为:

  • 依赖方向是:Presentation → Core(接口)
  • Presentation 使用自己的类型,这是正常的

🔍 对比其他服务和工厂

NavigationService(Infrastructure 层)

AuroraDesk.Infrastructure.Services.NavigationService
    ↓ 依赖
AuroraDesk.Core.Interfaces.IPageViewModelFactory  // ✅ 只依赖接口
AuroraDesk.Core.Entities.NavigationItem          // ✅ 依赖 Core 层

为什么可以? 因为它只使用接口,不依赖具体的 ViewModel 类型!

如果 PageViewModelFactory 没有页面的具体类型?

假设我们有一个"抽象"的工厂:

// 假设:不使用具体的 ViewModel 类型
public interface IPageViewModelFactory
{
    // 只返回接口,不涉及具体类型
    IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen);
}

问题:那工厂内部如何创建具体的 ViewModel?

  • 如果不用具体类型,就必须用反射
  • 失去了编译时类型检查
  • 需要维护复杂的映射配置

这就是为什么工厂通常依赖具体类型!

结论

PageViewModelFactory 在 Presentation 层是正确的!

依赖关系图

┌─────────────────────────────────────────┐
│  Core (核心层)                          │
│  - Interfaces/IPageViewModelFactory     │  ← 接口定义
└─────────────────────────────────────────┘
           ↑ 依赖                     ↑ 依赖
           |                          |
┌─────────────────────────┐  ┌──────────────────────────┐
│  Presentation           │  │  Infrastructure          │
│  - Services/            │  │  - Services/             │
│    PageViewModelFactory │  │    NavigationService     │
│      ↓ 实现接口         │  │      ↓ 使用接口          │
│  - ViewModels/Pages/    │  │                          │
│    DashboardPage...     │  │                          │
│    UsersPage...         │  │                          │
└─────────────────────────┘  └──────────────────────────┘
     ↑
     | PageViewModelFactory 使用
     |
 DashboardPageViewModel, UsersPageViewModel, etc.

为什么合理?

  1. PageViewModelFactory 实现了 Core 的接口
  2. PageViewModelFactory 使用 Presentation 层的类型
  3. 两者在同一层,没有跨层依赖问题
  4. Infrastructure 层通过接口使用,不依赖具体实现

📋 总结

位置 是否合理 原因
Infrastructure 违反依赖规则(Infrastructure → Presentation)
Core 违反依赖规则(Core 不能依赖其他层)
Application Application 不能依赖 UI 层类型
主项目 ⚠️ 可行,但会让主项目臃肿
Presentation 合理!与 ViewModels 同层,实现 Core 接口

所以,您说得完全正确! 🎉