第三章 整体架构分层方案:展示层、隔离层、模块内部状态层、跨模块通信层、持久化层
3.1 边界定义
跨模块边界状态隔离的核心在于为每个模块划定清晰的状态主权范围。本章定义的5层架构通过严格的接口契约实现物理与逻辑的双重隔离:
- 展示层 → 隔离层:仅允许通过预定义的
IViewModel接口读取不可变快照,不允许直接写状态。 - 隔离层 → 模块内部状态层:隔离层充当“门面”,只暴露原子化状态变更命令(Command),禁止暴露内部存储引用。
- 跨模块通信层:所有跨模块消息必须经过通信层序列化,消息体不可包含模块内部对象引用。
- 持久化层:状态快照的持久化由模块内部状态层发起,其他层只能通过隔离层订阅持久化结果事件。
边界约定:任何层不得直接使用其他模块的数据库连接、缓存实例或状态容器;所有跨层交互必须经过隔离层的接口签名校验。
3.2 分层策略
5层架构的设计动机源于职责解耦与依赖方向控制:
| 层级 | 职责 | 对外可见性 |
|---|---|---|
| 展示层 | 渲染UI、收集用户操作、展示状态快照 | 仅对当前模块 |
| 隔离层 | 定义模块对外接口、拦截非法访问、转换数据类型 | 全局暴露(但只允许通过接口) |
| 模块内部状态层 | 管理模块状态机、执行状态变更逻辑、触发副作用 | 仅对模块内部 |
| 跨模块通信层 | 消息路由、安全校验、序列化/反序列化 | 全局(通过消息总线) |
| 持久化层 | 本地存储、缓存策略、数据一致性 | 仅对模块内部状态层 |
依赖方向严格单向:展示层 → 隔离层 → 模块内部状态层 → 持久化层;跨模块通信层与所有层正交,仅处理消息传递。
3.3 组件职责
3.3.1 展示层(Presentation Layer)
- View:监听用户手势,调用 ViewModel 的输入方法。
- ViewModel:持有隔离层注入的 IModuleGateway 接口,通过该网关获取状态快照或下发命令。
- 生命周期与 UI 绑定,当 View 销毁时自动释放对网关的引用。
3.3.2 隔离层(Isolation Layer)
- IModuleGateway:每个模块对外暴露的唯一入口,包含只读状态查询方法与受限命令方法。
- TypeAdapter:将模块内部领域模型转换为跨模块安全的 DTO,防止引用泄露。
- AccessController:校验调用方模块 ID 是否在白名单中,拒绝未授权访问。
3.3.3 模块内部状态层(Internal State Layer)
- StateMachine:使用有限状态机管理模块核心流程,确保状态转换合法。
- Store:不可变状态容器,每次变更生成新快照,支持时间旅行动态调试。
- SideEffectRunner:处理异步副作用(网络请求、定时器),结果通过隔离层回传。
3.3.4 跨模块通信层(Cross-Module Communication Layer)
- MessageBus:基于主题(Topic)的发布/订阅机制,支持延迟消息、优先级队列。
- PayloadSerializer:将消息体序列化为 JSON/ProtoBuf,阻止对象引用传递。
- SecurityFilter:检查消息来源模块是否具备发送该主题的权限。
3.3.5 持久化层(Persistence Layer)
- LocalDatabase:使用 SQLite / Realm 存储状态快照,支持增量更新。
- CacheManager:内存LRU缓存,TTL过期策略,降低磁盘 I/O。
- SnapshotArchiver:定期将完整状态快照归档,用于崩溃恢复。
分层原则:每一层只依赖其直接下层接口(上层通过隔离层间接依赖),不允许跨层调用。跨模块通信层不依赖任何业务层,可被替换为不同实现(如本地总线 vs 远程RPC)。
3.4 数据流 / 控制流 / 信任链路
3.4.1 典型数据流(用户点击“提交订单”)
- 展示层的 Button 点击事件调用 ViewModel.submitOrder()。
- ViewModel 通过 IModuleGateway 发送
SubmitOrderCommand。 - 隔离层 AccessController 验证调用方模块 ID,TypeAdapter 将命令转换为内部领域命令。
- 模块内部状态层 StateMachine 校验当前状态是否为“待支付”,Store 生成新快照(状态变为“已提交”)。
- 持久化层异步将新快照写入本地数据库,成功后触发
OrderSubmittedEvent。 - 跨模块通信层将事件序列化后广播到其他模块(如物流模块、库存模块)。
3.4.2 信任链路
- 展示层 → 隔离层:信任基础是接口签名,展示层不能伪造隔离层的返回数据。
- 隔离层 → 模块内部状态层:隔离层持有模块内部状态层的弱引用,通过 DI 容器注入时只暴露接口,禁止直接 new 状态实例。
- 跨模块通信层:每个模块发送消息时必须携带模块签名 ,通信层解密后校验有效期。
- 持久化层:所有写入操作由模块内部状态层调度,持久化层不信任外部直接写入请求。
信任风险:若隔离层实现中存在错误类型转换,可能导致内部状态层收到畸形数据,引发状态机异常崩溃。因此必须为 TypeAdapter 编写完整单元测试,并启用运行时断言(Debug模式下)。
3.5 选型权衡
| 决策维度 | 方案 A | 方案 B | 推荐场景 |
|---|---|---|---|
| 隔离层通信方式 | 同步命令(Command) | 异步事件(Event) | 低延迟操作用同步,跨模块解耦用异步 |
| 跨模块序列化 | JSON(可读性强) | ProtoBuf(性能高) | 频繁通信用 ProtoBuf,调试环境用 JSON |
| 状态快照存储 | 全量快照 | 增量 Diff 日志 | 状态频率低用全量,高频变更用增量 |
| 模块间依赖 | 编译时接口依赖 | 运行时服务发现 | 稳定模块用编译时,动态模块用服务发现 |
关键取舍:隔离层的抽象程度越高,跨模块迁移成本越低,但运行时会增加一层代理开销。建议对性能敏感的模块(如地图渲染)使用轻量接口隔离(仅返回不可变快照),而非完全函数式门面。
3.6 风险点
- 循环依赖:模块 A 的展示层通过隔离层依赖模块 B,模块 B 又通过通信层反向调用模块 A → 必须通过 依赖方向审计 工具在 CI 阶段阻断。
- 状态泄漏:隔离层不小心返回了可变对象引用(如 List),导致外部直接修改内部状态 → 强制使用
Collections.unmodifiableList()或 DTO 深拷贝。 - 过度抽象:为每个模块设计独立的隔离层接口,导致大量样板代码 → 引入代码生成器(如 Kotlin Symbol Processor)自动生成网关实现。
- 跨模块通信风暴:多个模块同时广播事件,导致主线程阻塞 → 通信层必须实现背压(Backpressure)机制,对高频事件进行采样或合并。
3.7 落地步骤
- 定义接口契约:团队共同编写
IModuleGateway接口文档,明确每个方法的前置条件、返回类型、异常列表。 - 实现隔离层骨架:先实现 AccessController 和 TypeAdapter 空壳,确保编译通过。
- 建立模块内部状态层:为每个模块实现 StateMachine 和 Store,编写单元测试验证状态转换合法性。
- 集成跨模块通信层:搭建 MessageBus,配置 Topic 白名单,编写 SecurityFilter 模块签名校验。
- 串联端到端流程:选取一个简单场景(如“用户切换Tab”),验证展示层→隔离层→状态层→持久化层的完整链路。
- 压力测试与调优:模拟高频跨模块消息,调整序列化库、缓冲区大小、异步线程池参数。
- 文档与代码 Review 清单:输出《隔离层实现规范》和《跨模块消息设计模板》,强制纳入每次 MR 的 Review checklist。
3.8 验收标准
| 验收项 | 通过条件 | 验证方式 |
|---|---|---|
| 边界隔离 | 任何模块无法直接访问另一个模块的 Store 实例 | 反射扫描 + 运行时断言 |
| 接口兼容 | 展示层仅通过 IModuleGateway 交互,IDE 编译无跨模块直接引用 | 静态代码分析(ArchUnit) |
| 状态不可变性 | 外部获取的快照修改后不影响模块内部状态 | 深拷贝后的引用地址不同 |
| 通信安全性 | 未授权的模块发送消息被拦截并记录审计日志 | 集成测试模拟伪造 |
| 持久化一致性 | App 崩溃重启后,状态恢复为最近一次持久化快照 | 模拟 crash + 重启校验 |
验收工具:推荐使用 ArchUnit 编写分层依赖测试,在 CI 中自动运行,一旦检测到违反依赖方向则构建失败。