4. 模块内ViewModel状态定义:状态聚合根、不可变快照、单向数据流、本地状态作用域

4.1 边界定义:模块内状态管理四要素

模块内ViewModel的状态管理不是随意的属性集合,而是由四个紧密关联的架构要素构成的严格范式。这四个要素共同定义了“状态在模块内如何被定义、存储、变更和消费”的完整规则。

要素 定义 关键职责
状态聚合根 模块内部唯一的、权威的状态容器,封装所有业务状态 单一可信源,拒绝状态碎片化
不可变快照 对外发布的、一旦生成即不可修改的状态副本 线程安全、可追溯、可比较
单向数据流 状态变更遵循“事件→Reducer→新状态→UI”的闭环 可预测、可调试、可回放
本地状态作用域 状态的生命周期严格限定在模块内部,不对外暴露引用 防止外部副作用污染、支持模块卸载

4.2 分层策略:四要素的层级结构

四要素在模块内部形成三层架构,每一层只与相邻层通信:

  • 表现层(UI Layer):仅消费不可变快照,通过事件触发状态变更。
  • 状态管理层(State Layer):持有状态聚合根,调度Reducer产生新快照。
  • 作用域层(Scope Layer):负责模块生命周期管理,确保状态在模块销毁时被彻底释放。
架构提示: 三层之间的数据流必须经过类型擦除的接口,例如使用密封类(Sealed Class)或ADT(代数数据类型)作为事件载体,禁止直接传递内部状态引用。

4.3 组件职责:各要素的交付物

下表明确了每个架构要素在具体实现中的交付产物:

要素 交付物 验收标准
状态聚合根 单一数据类,包含模块全部业务状态字段 模块内无第二个独立的状态容器
不可变快照 数据类的只读拷贝(Kotlin data class / Swift struct) 所有字段为 val/let,无公开 setter
单向数据流 事件协议 + Reducer 纯函数 Reducer 无副作用,相同输入产生相同输出
本地状态作用域 模块级协程作用域或生命周期容器 模块销毁后,状态无法被任何外部引用访问

4.4 数据流与控制流

在四要素框架下,数据流与控制流严格分离:

  • 数据流:由聚合根向下游单向流动,经过不可变快照作用于UI。
  • 控制流:由UI发起事件,经Reducer改变聚合根状态,驱动数据流更新。

信任链路遵循“以聚合根为圆心,向外辐射”的原则:任何外部模块或代理都不被信任直接访问聚合根,必须通过事件接口间接通信。

// 伪代码:Reducer 纯函数示例
fun reduce(state: StateAggregate, event: Event): StateAggregate {
    return when (event) {
        is Event.UpdateTodo -> state.copy(
            todos = state.todos.map { 
                if (it.id == event.id) it.copy(isDone = true) else it 
            }
        )
        else -> state
    }
}

4.5 选型权衡与风险点

决策点 推荐方案 风险与规避
聚合根实现方式 单一不可变data class 避免使用mutable对象,防止外部引用修改
快照发布策略 响应式流(Flow/Observable) 注意背压和线程调度,防止状态“提前泄露”
作用域管理 显式取消/释放 小心内存泄漏,使用弱引用监听模块销毁
事件体积 扁平化事件结构 嵌套事件导致Reducer可读性差,利用模式匹配优化
常见陷阱: 状态聚合根膨胀。当模块业务逻辑复杂度上升时,聚合根可能包含过多字段。建议按业务领域拆分为子聚合,并使用组合模式重新组织。

4.6 落地步骤

  1. 划定状态边界:明确模块内的状态集合,排除UI无关的中间状态。
  2. 定义事件协议:使用密封类/枚举列出所有可能的状态变更事件。
  3. 实现Reducer:编写纯函数,确保每个事件对应唯一的状态转换逻辑。
  4. 创建不可变快照:在聚合根上暴露只读拷贝方法,或直接发布数据类副本。
  5. 注入作用域:将模块生命周期作用域与ViewModel绑定,确保销毁时清理。

4.7 验收标准

  • ✅ 模块内无法通过反射或强制转换修改聚合根内部状态。
  • ✅ 任意两次相同的事件序列产生的UI快照在结构和值上完全一致。
  • ✅ 模块销毁后,所有状态引用在GC root中不可达。
  • ✅ 外部模块无法订阅或观察模块内部的状态变化,除非通过桥接接口。
模块内ViewModel状态管理四要素架构 状态聚合根 StateAggregate 不可变快照 ImmutableSnapshot 单向数据流 Event → Reducer 本地状态作用域 LifecycleScope 核心约束 • 单一可信源 • 无外部引用修改 • 纯函数Reducer • 线程安全快照 • 生命周期绑定 • 可销毁/可重启 事件 新状态 UI消费 生命周期管理 模块边界 —— 内部状态对外不可见

4.8 四要素的信任关系与边界线

在模块内,四要素形成三条“不可逾越”的边界线:

  • 模块边界(模块 vs 外部):状态聚合根和本地作用域严格位于模块内部,外部只能通过桥接接口接收不可变快照,无法反向修改。
  • 数据流边界(聚合根 vs 快照):聚合根产生快照后,对原状态的控制权立即丧失;快照的消费者不能反向回写。
  • 对象边界(ViewModel vs 其他类):ViewModel本身不被视为状态的一部分,它只是作用域容器和Reducer调度器。
架构经验: 在大型项目中,建议为每个模块单独编写《状态定义清单》,逐字段标记其归属的聚合根子集、是否参与快照、生命周期类型。该清单应当作为代码审查的必检项。