设计目标:即使网络完全断开,客户端层+离线存储层依然能支撑用户完整操作;网络恢复后,同步中间层自动执行冲突检测与合并,最终与服务端层达成最终一致。
---
#### 3.2 四层职责定义
| 层级 | 核心职责 | 边界约束 | 典型技术选型 |
|------|---------|----------|-------------|
| **客户端层** | UI渲染、用户事件处理、本地读写、离线优先交互 | 不直接操作远端数据库;不执行业务复杂校验 | Flutter/SwiftUI/Compose + 本地数据库 |
| **同步中间层** | 增量变更捕获、双向同步管道、冲突检测与调解 | 不持有持久化状态;不直接生成业务数据 | CRDT/OT引擎、WebSocket长连接、幂等队列 |
| **服务端层** | 权威数据存储、认证授权、全局业务规则、多端合并 | 不感知客户端离线状态;不处理UI逻辑 | PostgreSQL/Spanner + gRPC/REST |
| **离线存储层** | 本地数据缓存、变更日志、离线队列、同步元数据 | 不驱动UI行为;不与网络层直接交互 | SQLite/WAL模式 + 日志结构化合并树 |
---
#### 3.3 数据流与信任链路
数据在四层间遵循**单向流动 + 环状回写**模式:
1. **写路径(客户端 → 离线存储 → 同步中间层 → 服务端层)**
- 用户操作 → 客户端写入离线存储(立即生效,乐观UI)
- 离线存储记录本地变更日志(`pending_changes` 表)
- 同步中间层轮询或监听变更,组装为增量包(`delta payload`)
- 网络可用时推送到服务端层,服务端验证后写入权威库
2. **读路径(服务端层 → 同步中间层 → 离线存储 → 客户端层)**
- 同步中间层订阅服务端变更事件
- 拉取增量更新,计算合并结果,写入客户端离线存储
- 客户端层通过本地数据库观察者模式刷新UI
3. **信任链路**
- 客户端层对离线存储层完全信任(本地写无需认证)
- 同步中间层与服务端层之间必须建立**双向认证TLS + 请求签名**
- 服务端层拒绝未签名的客户数据,仅接收同步中间层的合并结果
风险点:若同步中间层未对变更 payload 做幂等校验,网络重试可能导致服务端层重复写入。解决方案:同步中间层必须携带全局唯一 `sync_id`,服务端层建立去重索引。
---
#### 3.4 系统分层总图(内联SVG)
---
#### 3.5 选型权衡与落地步骤
##### 3.5.1 关键选型决策表
| 决策点 | 选项A | 选项B | 推荐场景 |
|--------|-------|-------|---------|
| 离线存储引擎 | SQLite + WAL | LevelDB/RocksDB | 关系型查询多选A,纯键值选B |
| 同步协议 | 基于 gRPC 双向流 | REST + 长轮询 | 高并发低延迟选gRPC,运维简单选REST |
| 冲突解决策略 | CRDT(无冲突数据类型) | 最后写入者胜出(LWW) | 协作编辑选CRDT,简单数据选LWW |
| 服务端权威数据库 | PostgreSQL | CockroachDB | 全局强一致选PG,跨区域多活选Cockroach |
##### 3.5.2 落地步骤(6阶段)
1. **定义层间契约接口**:明确 `SyncPayload`、`ConflictRecord`、`AckResponse` 的 protobuf 定义。
2. **搭建离线存储基础**:设计 `changes` 表(`sync_id`, `entity_type`, `entity_id`, `operation`, `data`, `timestamp`, `status`)。
3. **实现客户端层乐观UI**:写入离线存储后立即更新UI,不等待网络确认。
4. **构建同步中间层双向管道**:实现 `websocket_handler` 和 `retry_queue`。
5. **部署服务端层整合点**:编写 `merge_handler` 接收增量包,执行冲突检测。
6. **集成测试与调优**:模拟断网/弱网/高并发场景,验证最终一致性。
##### 3.5.3 验收标准
- **离线操作**:在完全断网环境下,用户可完成创建/编辑/删除全部核心实体,且本地UI无感知降级。
- **同步完整性**:重新联网后,离线期间所有操作均成功同步至服务端,无数据丢失。
- **冲突解决**:同时编辑同一字段时,系统按预设策略(CRDT/LWW)自动合并,不出现数据损坏。
- **性能边界**:单次同步延迟(从客户端写入到服务端确认)≤ 500ms(正常网络下)。
实践建议:优先将“离线存储层”的变更日志设计为追加写(append-only),避免随机写导致SQLite锁竞争。同步中间层使用
---
#### 3.6 风险点与边界法则
| 风险分类 | 具体场景 | 预防措施 |
|----------|---------|---------|
| **存储边界** | 离线数据库膨胀导致手机存储溢出 | 设置最大行数/容量,淘汰超期变更 |
| **信任边界** | 客户端伪造 `sync_id` 重放攻击 | 同步中间层验证 `sync_id` + 签名 |
| **时间边界** | 客户端时钟偏移导致变更顺序错乱 | 服务端层使用单调递增的 `server_timestamp` |
| **网络边界** | 同步中间层与服务端断连后,本地增量堆积 | 离线队列设置最大深度,超出后降级为只读 |
> **核心原则:每一层只信任自己下一层的输出。** 客户端层认为离线存储层的数据就是“当前状态”;同步中间层只信任服务端层返回的合并结果;服务端层拒绝任何非同步中间层来源的写入。
- 批量推送减少连接数
- 指数退避避免网络风暴