FastAPI 项目中的文件与 MongoDB 双存储

很多个人站、内容后台或内部工具一开始并不需要复杂数据库。文件存储启动快、部署简单、备份也直观;但当内容数量增加、多人维护、需要更复杂查询时,MongoDB 会更合适。双存储的重点不是同时写两份数据,而是让业务层只依赖统一仓储接口,部署时通过配置决定使用哪一种后端。

架构分层:让路由不关心数据来自哪里

建议把系统拆成三层:路由层负责 HTTP 输入输出,服务层负责内容筛选与渲染,仓储层负责具体读写。FastAPI 的 Depends 只暴露一个 ContentRepository,路由拿到的是抽象能力,而不是文件路径或 Mongo 集合。

仓储接口需要稳定

接口要围绕业务动作设计,比如 list_posts()save_project()get_settings()。不要把 Mongo 的查询参数或文件系统路径泄漏到上层,否则后续切换存储时会牵一发动全身。真正有差异的地方留在仓储实现内部处理。

文件存储:适合快速上线和可控备份

文件存储的优势是透明。文章正文可以放 Markdown,索引数据放 JSON,备份时复制整个内容目录即可。对于个人简历站,这种方式非常适合初始阶段:不需要额外数据库服务,也更容易用 Docker 卷持久化。

文件写入要注意原子性

文件写入不能直接覆盖目标文件。更稳妥的做法是先写临时文件,再用系统级替换操作覆盖目标文件。这样即使进程在写入中途退出,也能降低 JSON 索引损坏的概率。多请求同时写入时,还需要用锁保护同一个内容目录。

MongoDB:适合增长后的查询与协作

当文章、项目、留言数量变多,或者需要按时间、分类、状态做更复杂筛选时,MongoDB 的优势会很明显。它天然支持索引、条件查询和远程部署,也更适合多个服务实例共享同一份数据。

索引不要等到慢了再补

哪怕是轻量站点,也建议提前给常用字段建索引,例如文章 slug、发布时间,项目 slug,留言创建时间。slug 应该唯一,避免后台重命名或创建内容时覆盖已有记录。

配置切换:让部署决定存储模式

配置项可以保持简单:STORAGE_BACKEND=file 时使用内容目录,STORAGE_BACKEND=mongodb 时读取 MONGODB_URI 和数据库名。切换时,路由、模板和服务层不需要改代码。

推荐的切换原则

本地开发和早期部署优先用文件存储,方便排查和备份;正式多人维护、内容增长明显后再切 MongoDB。不要为了“看起来更企业级”过早引入数据库,否则会增加部署和运维成本。

迁移时要检查什么

迁移不是简单复制字段。需要确认正文 Markdown、发布时间、发布状态、精选状态、标签、项目成果、联系方式配置都能被完整保留。迁移后至少做三类验证:公开页面能正常访问、后台能编辑保存、站点地图和详情页链接没有断。

落地清单

  • 路由只依赖仓储接口,不直接操作文件或 Mongo 集合。
  • 文件存储使用原子写入和目录锁,避免并发写坏索引。
  • MongoDB 为 slug 和时间字段建立索引。
  • 通过环境变量切换存储,不改业务代码。
  • 迁移后跑公开页、后台保存、详情页、站点地图和健康检查。

双存储的价值不在“支持更多技术”,而在给项目保留演进空间。前期轻,后期稳,中间用统一接口把复杂度隔离住,这样系统才更容易长期维护。