一、数据流架构图
分层定义与职责边界
-
UI 层(View / Controller / Cell)
- 位置: ViewController.swift 、 DemoUI.swift 中的 FeedPostCell 、发布页 VC 等
- 职责:
- 管理 UIKit 生命周期、布局、交互(按钮、列表、prefetch)
- 将用户事件转交给 VM(如 refresh() 、 loadNextPageIfNeeded )
- 订阅 VM 输出(closure / delegate),驱动 UI 更新
- 禁止:
- 直接创建网络/持久实现( MockFeedAPI() / SQLitePostStore() )
- 直接访问全局单例( FeatureFlagCenter.shared 等),除非是组合根注入后的引用
-
VM 业务层(ViewModel)
- 位置:建议集中到 DemoModelsAndInfra.swift (或后续拆分到 Feed/ 、 Publish/ 目录)
- 职责:
- 业务编排:分页、弱网降级、限流、缓存读取/写入策略
- 输出 View 所需的最小状态(posts、loading、error message)
- 仅依赖协议: FeedAPI 、 PostStoring 、 NetworkMonitoring 、 FeatureFlagProviding
- 禁止:
- 引用 UIKit 类型(除非是图片等确有必要;FeedVM 当前已不依赖 ImageLoader)
- 直接写 UI(alert、label.text)
-
网络层(API / Service)
- 位置: DemoModelsAndInfra.swift (已有 FeedAPI/MockFeedAPI )、发布的 JPEGUploading/UploadService
- 职责:
- 请求、解析、错误封装
- 提供稳定协议接口给 VM: FeedAPI , JPEGUploading
- 约束:
- 不依赖 UI,不依赖数据库
-
持久层(Store / DB)
- 位置: DemoStorage.swift ( SQLitePostStore )
- 职责:
- 数据读写、迁移、线程安全封装
- 对外通过协议暴露: PostStoring
- 约束:
- 不知道页面、不知道分页策略(策略属于 VM)
-
基础设施层(Infra)
- 位置: DemoImages.swift ( ImageLoader: ImageLoading 、 ImageCompressor: JPEGCompressing )、 DemoModelsAndInfra.swift (FeatureFlag、NetworkMonitor、Analytics、RateLimiter)
- 职责:
- 提供跨业务可复用能力(缓存、监控、埋点、限流、图片加载)
- 对外用协议抽象,业务层通过注入使用
依赖关系规则(强约束)
- 允许依赖方向(上层依赖下层的抽象):
- UI → VM(具体 VM 类型或协议均可)
- VM → 协议(Network/Persistence/Infra 的 protocol)
- Infra/Network/Persistence → Foundation/系统库
- 禁止反向依赖:
- Network/Persistence/Infra 不得 import UIKit(图片除外已在 DemoImages.swift)
- VM 不得依赖 UI 组件(UIViewController/UILabel/UITableView 等)
- 组合根唯一入口:
- SceneDelegate 负责创建“具体实现”,并注入到 VC/VM(Composition Root)
基于 ViewController.swift 的落地设计
1) ViewController 的依赖注入(已落地)
- 当前 ViewController 已按分层方案注入:
- FeedViewModel (VM)
- ImageLoading (infra)
- FeatureFlagProviding (infra)
- AnalyticsTracking (infra)
- 价值:
- UI 层不再负责组装依赖;可在测试中注入 fake/mock
2) UI 与 VM 的通讯机制
-
VM → UI:closure 回调(当前已有)
- onStateChanged :驱动 table reload
- onError :UI 展示 alert
-
UI → VM:直接调用 async 方法(当前已有)
- loadInitial/refresh/loadNextPageIfNeeded 建议规范化:
-
VM 输出尽量不直接暴露整个 VM(当前 onStateChanged: (FeedViewModel)->Void ),改成输出结构化 State (降低 UI 对 VM 内部字段耦合)。
-
UI 的刷新与分页触发由 UI 控制,业务节流/降级由 VM 控制。
3) Cell 与业务的边界
- FeedPostCell.configure 依赖:
- ImageLoading :拉图/取消
- AnalyticsTracking :曝光/加载成功失败埋点
- 这样 Cell 不需要知道 ImageLoader.shared 或 AnalyticsTracker.shared
Demo: Github