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
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 层内部的事
关键理解:
- ✅ 接口在 Core 层定义(IPageViewModelFactory)
- ✅ 实现可以在 Presentation 层(PageViewModelFactory)
- ✅ 使用同层的具体类型(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.
为什么合理?
- ✅
PageViewModelFactory实现了 Core 的接口 - ✅
PageViewModelFactory使用 Presentation 层的类型 - ✅ 两者在同一层,没有跨层依赖问题
- ✅ Infrastructure 层通过接口使用,不依赖具体实现
📋 总结
| 位置 | 是否合理 | 原因 |
|---|---|---|
| Infrastructure | ❌ | 违反依赖规则(Infrastructure → Presentation) |
| Core | ❌ | 违反依赖规则(Core 不能依赖其他层) |
| Application | ❌ | Application 不能依赖 UI 层类型 |
| 主项目 | ⚠️ | 可行,但会让主项目臃肿 |
| Presentation | ✅ | 合理!与 ViewModels 同层,实现 Core 接口 |
所以,您说得完全正确! 🎉