外观
入口与运行时装配
后端装配边界回答的是:应用是如何被创建的,运行时依赖是如何进入系统的,FastAPI dependency 又是如何从 app.state 中取回这些依赖的。
关键文件
| 文件 | 当前职责 |
|---|---|
app/main.py | 暴露 app = create_app(),保留少量兼容导出 |
app/bootstrap.py | backend composition root |
app/runtime.py | app.state 绑定与 resolver |
app/api/runtime_dependencies.py | runtime 依赖的 FastAPI adapter |
装配链路
入口导出main.py
应用装配create_app()
基础设施实例settings / db / storage / rag
运行时绑定bind_runtime_context
↴
应用状态app.state
接口依赖适配runtime_dependencies.py
业务接口app/api/*
main.py
当前职责非常克制:
- 从
bootstrap.py导入create_app - 从
bootstrap.py透传build_cors_config - 实例化
app = create_app()
这说明 main.py 的角色主要是:
- 暴露应用入口
- 给现有测试提供兼容导出
bootstrap.py
create_app() 当前完成的装配动作包括:
- 获取
Settings - 创建 FastAPI app
- 构建 DB engine 与 session factory
- 创建
AvatarStorageService - 通过
bind_runtime_context()把 runtime 对象挂到app.state - 配置 static delivery
- 配置 CORS
- 注册 router
- 注册
/与/health
router 注册事实
当前 ROUTER_REGISTRATIONS 中包含:
- auth
- user
- safety
- chat
- prescription
- sport
- companion
- assessment
其中 companion debug router 只在 DEVELOPMENT_MODE 下额外注册。
runtime.py
绑定函数
bind_runtime_context() 负责把以下对象挂到 app.state:
settingssession_factorydb_enginerag_engine_getteravatar_storage_service
resolver
runtime.py 同时提供:
resolve_runtime_settings()resolve_session_factory()resolve_db_engine()resolve_rag_engine()resolve_avatar_storage_service()
这些 resolver 的策略是:
- 优先读
app.state - 缺失时回退到默认全局对象
runtime_dependencies.py
这是 runtime 边界对 FastAPI adapter 的薄封装。
当前主要导出:
get_sms_service()get_avatar_storage_service()
它们通过 Request.app 拿到 app,再调用 runtime resolver,避免 API router 直接拼装依赖。
为什么这个边界重要
- 如果 runtime 依赖重新散落到 router/service 内部,系统会很快回到“看起来能跑,但难以替换、难以测试、难以交接”的状态。
- 当前这套装配方式让 settings、session、db、rag、storage 至少都有一个明确读取入口。
- 后续新增基础设施依赖时,优先考虑是否应进入
bootstrap/runtime,而不是在业务代码里随手 new。
新增 runtime 依赖准入
| 判断问题 | 推荐处理 |
|---|---|
| 依赖是否跨多个 router / service 使用 | 是,则优先进入 bootstrap/runtime 统一装配 |
| 依赖是否需要测试替换 | 是,则提供 resolver 或 FastAPI dependency |
| 依赖是否持有连接池、client、存储实例 | 是,避免在 route 内反复创建 |
| 依赖是否只服务单个局部纯函数 | 否,不需要进入 runtime,可留在局部 helper |
| 依赖是否包含密钥、连接串或外部 provider client | 需要从 settings 读取,并确保日志不输出敏感值 |
判断 runtime 边界时,重点不是“能不能 import 到”,而是依赖生命周期、替换方式和测试入口是否清楚。
来源锚点
apps/backend_service/app/main.pyapps/backend_service/app/bootstrap.pyapps/backend_service/app/runtime.pyapps/backend_service/app/api/runtime_dependencies.py