# Aligner 底层代码架构方案 v1 > 三位架构师合议共识 | 2026-05-23 > 架构师 A(系统架构) + B(状态/数据流) + C(底层实现) > 接下来交给 Codex 实施 --- ## 一、架构总览(共识) ``` ┌───────────────────────────────────────────────┐ │ VIEW LAYER (SwiftUI) │ │ ContentView, SpaceCardView, FallZone, │ │ AppShelf, EditModeContainer, ConfirmSheet, │ │ SixPlusOneGridPreview │ │ 读取: @EnvironmentObject var browseVM / editVM │ │ 禁止: 直接 @EnvironmentObject var appState │ └───────────────────┬───────────────────────────┘ │ 读取派生数据 ▼ ┌───────────────────────────────────────────────┐ │ VIEWMODEL LAYER │ │ │ │ BrowseViewModel EditViewModel │ │ ├ groups apps ├ selection state │ │ ├ filters windows ├ state machine │ │ └ hover state └ apply orchestrator │ │ │ │ 输入: AppState (只读) │ │ 输出: 派生数据 + 用户操作命令 │ └───────────┬───────────────────┬───────────────┘ │ 读写 │ 操作命令 ▼ ▼ ┌───────────────────────────────────────────────┐ │ STORE LAYER (AppState ~80行) │ │ │ │ @Published private(set): │ │ - windows: [WindowInfo] 只读快照 │ │ - spaces: [SpaceInfo] 只读快照 │ │ - displays: [DisplayInfo] 只读快照 │ │ - apps: [AppInfo] 只读快照 │ │ - permissionStatus: PermissionStatus │ │ - moveReport: MoveReport? │ │ - lastError: WindowServiceError? │ │ ❌ 不再包含业务逻辑 │ │ ❌ 不再持有服务实例 │ └───────────┬───────────────────┬───────────────┘ │ 刷新数据 │ 操作结果 ▼ ▼ ┌───────────────────────────────────────────────┐ │ SERVICE LAYER │ │ │ │ DataService LayoutExecService │ │ ├ refreshWindows() ├ applyLayouts() │ │ ├ loadAXWindows() ├ async 方法 │ │ ├ loadCGWindows() ├ 重试+超时 │ │ └ rebuildDisplays() └ 返回 MoveReport │ │ │ │ PermissionService (Gate, 去Manager) │ └───────────┬───────────────────────────────────┘ │ AX API ▼ ┌───────────────────────────────────────────────┐ │ INFRASTRUCTURE LAYER │ │ │ │ WindowService (合并Operator, 直接AXUIElement) │ │ PermissionGate (单层) │ │ CGWindowListProvider (fallback) │ │ FrameMath (纯函数几何运算) │ └───────────────────────────────────────────────┘ ``` --- ## 二、关键共识(三位一致同意) ### 2.1 AppState 瘦身(A+B+C 共同认定) 从 **493 行 → ~80 行纯数据容器**。移出: - `DataService` → 承担数据加载(loadAX/CG/Build/Refresh) - `LayoutExecutionService` → 承担布局执行(applyConfirmedLayouts) - `EditViewModel` → 承担编辑状态机 - `BrowseViewModel` → 承担界面派生数据 - `FrameMath` → 承担几何运算工具函数 ### 2.2 PermissionManager 删除(A+C 发现, B 确认) - AppState 直接持有 `PermissionGate` - 删除 `PermissionService.swift`(45行纯委托文件) - `@Published permissionStatus` 从 `gate.$status` 单源订阅 ### 2.3 WindowService/Operator 合并(A+C 发现, B 确认) - 合并 `WindowService` 协议 + `AXOperator` 实现 → 单个 `WindowService` 协议(或 struct) - **去掉 AXApplication/AXWindow 值类型和 handle 映射层** - 直接使用 `AXUIElement` 引用 - 降低 3 文件 → 1 文件,减少 mock 维护量 ### 2.4 编辑状态机用 enum(A+B 共识) ```swift enum EditModeState: Equatable { case inactive case entering case active(EditSession) case confirming case applying case applied(MoveReport) case failed(WindowServiceError) } ``` 移到 `EditViewModel`,从 AppState 中删除 `editState`。 ### 2.5 applyConfirmedLayouts 改为 async(C 发现, A+B 确认) - `Thread.sleep(forTimeInterval: 0.3)` → `Task.sleep(for: .milliseconds(300))` - 方法签名从 `@discardableResult func ... -> MoveReport` 改为 `func ... async -> MoveReport` - 添加重试逻辑(最多3次,带指数退避) --- ## 三、分歧与裁决 | 议题 | A 意见 | B 意见 | C 意见 | 共识结果 | |------|--------|--------|--------|---------| | AppState 是否仍为单例 | 单例,抽出业务逻辑 | 单例,纯数据容器 | 单例,保留 | ✅ 保留单例 | | ViewModel 数量 | 每个视图一个 | Browse + Edit 两个 | 同 B | ✅ 两个 ViewModel | | 编辑状态机位置 | AppState | EditViewModel | EditViewModel | ✅ EditViewModel | | DataService 是否需要 protocol | 需要(可测试) | 不需要(纯函数组合) | 需要 | ✅ 保留 protocol | | SPM 拆包时机 | P0 | P3 | P2 | ✅ 推迟到 P3 | --- ## 四、文件变动清单(Round 5) | 操作 | 文件 | 说明 | |------|------|------| | ✏️ 重写 | `Store/AppState.swift` | 从 493 行压缩到 ~80 行纯数据容器 | | ✏️ 重写 | `Services/WindowService.swift` | 合并 WindowOperator 协议, 去 handle 映射 | | 🗑️ 删除 | `Services/WindowOperator.swift` | 逻辑移入 WindowService | | 🗑️ 删除 | `Services/AXWindowService.swift` | 逻辑移入 WindowService | | 🆕 新建 | `Services/DataService.swift` | 从 AppState 提取数据加载/刷新 | | 🆕 新建 | `Services/LayoutExecutionService.swift` | 从 AppState 提取 applyConfirmedLayouts | | 🆕 新建 | `ViewModels/BrowseViewModel.swift` | 派生数据:filter/group/hover | | 🆕 新建 | `ViewModels/EditViewModel.swift` | 编辑状态机 + 编排 | | 🆕 新建 | `Utils/FrameMath.swift` | 几何运算(intersectionArea 等) | | 🗑️ 删除 | `Services/PermissionService.swift` | PermissionManager 直接删除, Gate 由 AppState 持有 | | ✏️ 小改 | `Models/EditModels.swift` | 编辑状态机用 enum 替代部分 struct | | ✏️ 小改 | 所有 Views(9个文件) | @EnvironmentObject appState → @EnvironmentObject browseVM/editVM | ### 文件变更统计 | 指标 | 数值 | |------|------| | 新建文件 | 4 个(DataService, LayoutExecService, BrowseVM, EditVM, FrameMath) | | 重写文件 | 2 个(AppState, WindowService) | | 删除文件 | 3 个(WindowOperator, AXWindowService, PermissionService) | | 小改文件 | ~10 个(Views, EditModels) | | 净代码行变化 | ~800 行增 ↔ ~700 行减(总行数微增但职责清晰) | --- ## 五、实施顺序 ``` Round 5a: 基础设施层重构(核心,不影响UI) 1. 删除 PermissionService(PermissionManager 文件) 2. 合并 WindowService + WindowOperator + AXWindowService 3. AppState 直接持有 PermissionGate 4. 创建 FrameMath(从 AppState 抽出工具函数) ✅ Build + Test 验证 Round 5b: Service 层提取(中等影响) 5. 创建 DataService(从 AppState 抽出数据加载) 6. 创建 LayoutExecutionService(从 AppState 抽出布局执行) 7. 修改 AppState 从 493 行压缩到 ~80 行 ✅ Build + Test 验证 Round 5c: ViewModel 层创建(影响UI) 8. 创建 BrowseViewModel(View 改 @EnvironmentObject) 9. 创建 EditViewModel(附加状态机迁移) 10. 9个 View 文件改为依赖 ViewModel ✅ Build + Test 验证 + QA 门神 Round 5d: 异步化 + 重试 11. applyConfirmedLayouts → async 12. Thread.sleep → Task.sleep 13. AX 调用加重试 ✅ Build + Test 验证 + QA 门神 + PO 验收 ``` --- *本文件由三位架构师合议产出,由 PM 归档。*