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.

199 lines
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)
```
### 关键代码
```csharp
// 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 没有页面的具体类型?
假设我们有一个"抽象"的工厂:
```csharp
// 假设:不使用具体的 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 接口 |
**所以,您说得完全正确!** 🎉