第4章 分层数据流架构:UI层→ViewModel层→Domain层→Data层,反向数据流驱动

在 KMP + Compose 的状态共享实践中,反向数据流(Unidirectional Data Flow,UDF)是核心原则。本层架构将系统划分为四个职责清晰的层次,强制事件向上传递、状态向下流动,从而形成可预测、可测试、可追踪的状态变更链路。反向数据流并非技术选项,而是架构纪律。

核心认知:每一层只信任下层提供的稳定状态,下层不信任上层的业务判断。信任链路沿 Data → Domain → ViewModel → UI 单向建立,反向只能通过事件(Event)驱动。

4.1 边界划分与分层策略

层次职责依赖方向共享状态范围
UI纯 Compose 组件,渲染状态,收集用户操作并包装为事件仅依赖 ViewModel单个 Composable 或 Screen
ViewModel管理界面状态(StateFlow),协调 Domain 层,格式化数据供 UI 消费仅依赖 DomainViewModel 实例(与 Lifecycle 绑定)
Domain纯业务逻辑,包含 UseCase、数据校验、聚合计算仅依赖 DataUseCase 级别(无状态或缓存状态)
Data数据源操作:本地(Room/SQLDelight)、远程(API/GraphQL)、缓存策略无上层依赖Repository / DataSource 级别

4.2 组件职责与数据流/控制流/信任链路

  • 数据流:Data → Domain → ViewModel → UI,状态从最底层逐层转换、映射、裁剪为 UI 友好的形式。
  • 控制流(事件流):UI 产生事件(Action/Intent),经 ViewModel 转为业务请求,Domain 编排执行,最终流向 Data。
  • 信任链路:每一层只信任直接下层的公开接口;Domain 不信任 ViewModel,Data 不信任 Domain——信任由接口契约保证,不依赖运行时状态。
分层反向数据流架构 User / 用户 UI (Compose) ViewModel Domain (UseCase) Data (Repository) 反向数据流驱动 事件向上 · 状态向下 信任链路单向建立

4.3 选型权衡

维度分层 UDF 优势潜在代价
可测试性每一层可单独 Mock 下层接口,单元测试覆盖率高需为每层编写接口和测试桩,初期成本略高
状态可预测状态变更路径唯一,调试时仅需追踪 StateFlow 链路跨层状态同步需额外映射,可能产生少量样板代码
平台差异隔离Domain/Data 层纯 Kotlin,完全复用;UI 层仅依赖 Compose MultiplatformViewModel 层需处理 Lifecycle 平台差异(如关闭协程)
团队协作边界清晰,前端、业务、数据团队可并行开发不同层需要严格的代码 Review 保证分层洁癖

4.4 风险点

  • 事件泛滥:UI 层频繁发送微小事件导致 ViewModel 重复计算 → 使用 debounce / distinctUntilChanged 收敛。
  • 状态穿透:ViewModel 直接暴露 Data 层内部状态(如 HTTP 错误码) → 强制在 Domain 层做领域转换。
  • 协程泄露:ViewModel 层协程作用域未与 Lifecycle 正确绑定 → 使用 viewModelScope 或 lifecycleScope。
  • 过度抽象:简单 CRUD 场景强行分层 → 适当采用扁平架构(仅 UI + Data),但须在文档中显式声明。

4.5 落地步骤

  1. 定义层间接口契约:创建 Repository 接口(Data 层)、UseCase 接口(Domain 层)、ViewModel 接口(ViewModel 层)。
  2. 实现 Data 层:先写本地/SQLDelight 数据源,再提供 Fake/Mock 实现供上层测试。
  3. 实现 Domain 层:基于 Data 接口编写 UseCase,仅暴露领域状态(如 Sealed Class)。
  4. 实现 ViewModel 层:注入 Domain 层,用 MutableStateFlow 管理 UI 状态,接收事件并映射为 Domain 调用。
  5. 实现 UI 层:收集 ViewModel 的 StateFlow,渲染 Compose 组件,将交互封装为 sealed 事件。
  6. 验证反向数据流合规:Code Review 时检查是否有下层直接修改上层状态,或 UI 直接调用 Data 层。

4.6 验收标准

验收项判断标准
状态流单向性编译期无上层直接引用下层可变状态(除 ViewModel 的 StateFlow)
事件流单向性UI 至 Data 的事件传递全部经过 ViewModel → Domain → Repository 接口
信任链完整性每层的公开接口仅包含 suspend 函数或 Flow,不暴露内部状态引用
可替换性任意层的实现可通过 Koin/Kodein 替换为 Mock 或另一平台实现,不影响其他层编译
调试体验启用 Compose 状态检查工具时,能清晰看到每个 StateFlow 的订阅者和发射源
常见反模式:在 UI 层直接创建 ViewModel 的 StateFlow 并手动 update。这破坏了反向数据流——状态必须由 ViewModel 统一管理,UI 只能通过事件请求变更。