1. 状态共享架构全景:领域分层、平台边界、跨模块依赖、统一数据源设计

在 KMP + Compose 项目中,状态共享不仅仅是“多个界面读取同一变量”,而是涉及领域模型隔离、平台能力桥接、模块间依赖控制、数据源单一事实来源四个核心维度的系统设计。本节从架构顶层绘制全景,为后续章节的每一层拆解提供坐标系。

1.1 四层核心职责矩阵

架构层 职责 平台边界 共享范围
UI / Compose 纯展示、用户事件映射、Compose 生命周期绑定 Android / iOS 各自宿主 Activity/ViewController 不可共享(只同平台)
Domain(领域层) 业务逻辑、用例编排、状态转换规则 纯 Kotlin,无平台 API 依赖 所有模块共用
Data(数据层) 数据存储、网络请求、缓存、同步策略 KMP 对应平台实现(expect/actual) 模块间隔离,通过统一接口访问
平台桥接层 传感器、文件系统、推送等原生能力适配 Android / iOS 各自 actual 实现 仅对 Domain 暴露抽象接口

每层之间有严格的单向依赖:UI → Domain → Data → Platform。任何反向依赖(例如 Domain 直接依赖 UI)都会破坏架构的可测试性和复用性。

1.2 统一数据源设计原则

为了避免“多处写、读不一致”,统一数据源要求所有状态变更都通过唯一的仓储(Repository)入口,且 Domain 层只感知抽象的 Flow<T> 或 StateFlow<T>。实现策略包括:

  • 本地优先:写操作先写入本地数据库(如 SQLDelight),再异步同步到远端。
  • 跨模块引用:通过 Gradle 模块依赖声明 api 或 implementation,避免循环依赖。
  • 状态收敛:一个业务实体只由一个 Repository 管理,不允许不同模块缓存同实体的两份副本。
关键认知:统一数据源不是强制所有数据放在同一个数据库,而是保证“任何一条数据只有一个权威写入路径”。

1.3 状态共享架构全景图

下图展示了 KMP 项目中状态共享涉及的领域分层、平台边界、模块依赖和数据流方向。节点颜色对应架构层,箭头代表数据流与控制流方向。

KMP Compose 状态共享架构全景 UI 层(Compose) Android Compose UI iOS Compose UI (UIKit 宿主) UI 层(Compose) Android 端 UI 逻辑 iOS 端 UI 逻辑 Domain 层(共享业务逻辑) Use Case · State 转换 · 跨平台业务规则 纯 Kotlin,无平台依赖 Data 层(仓储/网络/本地) 统一 Repository · SQLDelight · Ktor · DataStore 平台桥接层(Android) actual 实现:FileIO / 传感器 平台桥接层(iOS) actual 实现:FileIO / 通知 统一数据源:单一事实来源

1.4 数据流与控制流分解

状态共享中的信任链路遵循“向下依赖接口,向上推送事件”:

  • 数据流:Data 层通过 Flow 向上发送状态快照 → Domain 层做去重、合并、业务转换 → UI 层通过 collectAsState() 驱动重组。
  • 控制流:用户操作 → ViewModel 调用 Domain UseCase → UseCase 调用 Repository → Repository 决定写本地还是远端。
  • 信任链路:UI 信任 Domain 返回的状态是正确且唯一的;Domain 信任 Data 层的 Repository 是统一入口;Data 层信任平台桥接提供的原生能力符合预期。

1.5 选型权衡与风险点

关注点 选型建议 风险
状态容器 StateFlow + MutableStateFlow(Compose 原生支持) 初学容易混用 LiveData 或 RxJava,增加平台耦合
跨模块依赖 Gradle 模块分离 + api/implementation 管控 如果没有 dependency guard,容易产生循环依赖或版本冲突
数据同步一致性 本地优先 + 乐观更新 + 冲突解决策略 网络离线时 UI 显示过期数据,用户感知不一致
平台桥接层 使用 expect/actual 声明,Domain 层只依赖接口 expect/actual 无法跨模块共用,需放到 commonMain 的同名包下
避坑提示:不要为了“共享”而将所有状态提升到 Domain 层。只有业务相关的、需要跨平台复用的状态才放在 Domain;纯 UI 状态(如动画参数、列表滚动位移)应保留在 UI 层以保持 就近原则。

1.6 落地步骤(参考)

  1. 划分模块边界:创建 :shared、:data、:domain、:ui 四个 Gradle 模块,在 settings.gradle.kts 中声明依赖关系。
  2. 定义统一数据源接口:在 :data 模块中定义 UserRepository 接口及其 expect 声明。
  3. 实现平台 actual:在 :androidApp 和 :iosApp 中提供 SQLDelight 驱动的 actual 实现。
  4. Domain 层编排业务:编写 UseCase,只依赖 Repository 接口和 Kotlin 协程。
  5. UI 层消费状态:通过 viewModelScope 收集 StateFlow,使用 collectAsState() 驱动 Compose 重组。
  6. 集成验收:编写跨平台单元测试(JVM + iOS 模拟器)验证数据流完整性。

1.7 验收标准

  • 分层独立测试:Domain 层的 UseCase 不依赖任何 Android/iOS 框架,可在纯 JVM 环境运行。
  • 状态一致性:同一个 Entity 在 Android 和 iOS 上修改后,两边数据最终一致(延迟不超过 2s)。
  • 无循环依赖:Gradle 依赖图没有反向边,gradle dependencies 输出正常。
  • 平台边界清晰:所有 platform-specific 代码集中在 actual 模块,commonMain 中无 android 或 UIKit 类型引用。
  • 统一数据源写入唯一:通过代码审查确认没有绕过 Repository 直接写入数据库或文件。