gwy-exam/deliverables/architecture/decisions/ADR-003-json-datastore.md
2026-08-26 16:20:55 +08:00

50 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-003: 使用本地 JSON 文件作为数据层(非 SQLite
## Status: Accepted (2026-08-26)
## Background
「备考中枢」是**个人单用户、自用型**工具,数据是用户的长期备考资产(题库/错题本/作答记录/模考/申论/计划/统计)。用户已明确选 **JSON 文件存储**(非 SQLite。需要数据可读、可迁移、可备份并支持 Docker volume 挂载持久化。
## Decision
采用**本地 JSON 文件**作为数据层:每实体一个 JSON 文件(`questions.json` / `wrong-questions.json` / `mock-exams.json` / `tasks.json` / `shenlun.json` / `answer-records.json` / `stats.json` / `settings.json` / `meta.json`),存放在可配置数据目录 `APP_DATA_DIR`Docker volume 挂载点)。
核心设计(见 architecture.md §六):
1. **每实体一个文件 + 顶层索引结构**`{ version, updatedAt, index: {id→下标}, items: [] }`
2. **数据形状**:全部由 `packages/shared/src/schemas/*.schema.ts` 的 zod schema 定义,经 `z.infer` 产出强类型,无 any状态用字符串字面量联合。
3. **原子写入**:临时文件 + `fs.rename` 原子替换(同文件系统 rename 原子)。
4. **写队列串行**:单进程内所有写操作入 Promise 队列逐条执行,杜绝并发写坏文件。
5. **防抖批写**:高频写(刷题每道题)合并落盘,内存立即生效。
6. **zod 运行时校验**:文件是可变外部输入,读入后过 schema 收窄为强类型实体。
7. **统计用内存聚合 + 快照**:不每次全量扫描文件。
8. **数据资产能力**:预留一键导出 JSON/CSV + 备份/还原接口。
### 为什么对个人工具合理(正面)
| 维度 | 说明 |
|---|---|
| 单机低运维 | 零安装、零连接、零迁移脚本Docker 一个容器即用 |
| 数据资产可读可迁移 | 纯文件可 cat/jq 直读、可 git 跟踪、可一键备份,跨设备无锁定 |
| 开发快 | 无 ORM/迁移/连接池,`data/store.ts` 单点 fs 读写MVP 工作量最小 |
| DevOps 极简 | Docker volume 挂载即持久化,备份即拷贝目录 |
### 代价与规避(已正视)
| 代价 | 规避 |
|---|---|
| 并发写 | 写队列串行(互斥) |
| 写入非原子 | 临时文件 + rename 原子替换 |
| 无事务 | 单文件原子 + 组合操作分解为最小写单元 + 统计可重算(不强一致) |
| 体积增长 | 按需惰性加载 + 作答记录归档分片(按月切文件) |
| 统计 O(n) | 内存聚合 + 快照持久化 |
## Consequences
- 正面:数据资产感强、运维极简、开发快、零基础设施成本。
- 负面:无事务、无查询索引、并发写入需手动防护、数据量大后需归档分片。
- 权衡:对个人单用户量级(年 1-2 万题,约 10-20 MB JSON完全无压力。**本期不需要 SQLite/PostgreSQL**。若未来出现多设备协作 / 超百万条 / 复杂关系查询,再以新 ADR 追加迁移(本期不预留迁移负担)。
## Related ADRs
- ADR-001Express 5
- ADR-004zod 运行时校验——数据来自 JSON 文件的关键保障)
- ADR-005monorepo