网络与数据层
分层职责
| 模块 | 放什么 | 不放什么 |
|---|---|---|
core:network | API 接口、DTO、mapper、拦截器、OkHttp/Retrofit 装配 | Compose、ViewModel |
core:database | Entity、DAO、Room Database、schema | 业务 UI 状态 |
core:datastore | Token、Admin Token、端点、用户偏好 | 业务列表缓存 |
core:model | 跨层 domain 模型 | Wire 专用字段细节(除非刻意共享) |
core:data | Repository 接口 + OfflineFirst / Network 实现 | 直接暴露 DTO 给 UI |
UI 与 ViewModel 只依赖 Repository 与 model。
Offline-first 请求节奏
以设备列表为例(OfflineFirstDevicesRepository):
- 观察
observeDevices(organizationId): Flow<List<Device>>
← RoomDeviceDao.observeDevices - 刷新
refreshDevices(...): AppResult<DevicesPage>
← RetrofitDevicesApi→ 映射 →cacheDevices写入 Room - UI 一直收集 Flow;刷新成功后列表自动更新
写操作(创建、更新、启停)同样:调 API → 更新缓存 → 返回 AppResult。
UI ──refresh──► Repository ──Retrofit──► API
▲ │
│ ▼
└──observe Flow── Room / 其它本地源
结果类型
网络与可失败操作用 AppResult(core:common)表达成功/失败,避免到处 try/catch 冒泡到 UI。
展示给用户的文案优先用后端 ErrorResponse.error.message;code / details 留给诊断或 debug。
鉴权与 401
| 组件 | 行为 |
|---|---|
AuthInterceptor | 附加 Bearer access token(进程内缓存,必要时回源 DataStore) |
TokenAuthenticator | 401 时 单飞 refresh;并发请求复用新 token |
| Refresh 自身 401 | 不再递归刷新;清空本地会话 |
AdminTokenInterceptor | 仅 admin 路径附加 X-Admin-Token |
登出 / refresh 失败后,导航应回到 Auth 图(由 app 层会话观察驱动)。
JSON 与 DTO
- 新网络模型优先 kotlinx.serialization + 生成序列化器
- 单例
Json:ignoreUnknownKeys = true等(见core:network实现) - DTO 留在 network 层;经 mapper 变成
core:model再给 Repository 外溢 - 不要把 Retrofit 接口塞进 feature 模块