创建时间: 2026-09-01最后更新: 2026-09-01作者: yangbo(2d5d2525a)
1. 分层解决什么问题
在只有一两个接口时,直接在路由函数中执行 select() 很容易理解。随着项目逐渐出现用户、项目、成员、任务和权限规则,路由如果同时负责参数校验、权限判断、事务控制和数据库查询,很快就会变得难以修改和测试。
分层不是为了增加文件数量,而是让不同类型的变化停留在各自的位置:
| 层 | 主要职责 | 不应该负责 |
|---|---|---|
| Route | HTTP 路径、参数、依赖和状态码 | 拼接数据库查询、决定事务提交 |
| Schema | 请求校验与响应结构 | 表关系、数据库迁移 |
| Service | 业务规则、权限检查和事务边界 | 依赖 Request、HTTPException |
| Repository | 查询与持久化操作 | HTTP 响应、完整业务流程 |
| Model | 表、列、约束和 ORM 关系映射 | 接口输入输出 |
以“创建任务”为例,HTTP 层需要知道接口是 POST /api/v1/projects/{project_id}/tasks,成功时返回 201;业务层需要保证当前用户是项目成员,并且任务编号在项目内唯一;持久化层则负责把这些要求转换成 SQLAlchemy 查询。
这些职责之间只有一个主要依赖方向:
dependency-direction.txt
1HTTP 请求2-> Route + Schema3-> Service4-> Repository5-> AsyncSession + Model6-> PostgreSQL
返回结果沿相反方向传回,但代码依赖不应该倒置。例如,Repository 不应导入 TaskCreate,否则数据库访问层会被 HTTP 请求结构绑定;Service 也不应抛出 HTTPException,否则它很难在后台任务或测试中复用。
分层也不是机械规则。只有一条简单查询的模块可以先用函数组织;当业务规则、事务或查询开始重复时,再提取 Service 或 Repository。判断标准是职责是否清楚,而不是文件数量是否足够多。
2. 目录与请求流
可以把数据库相关代码组织成下面的结构:
projects.py项目路由
tasks.py任务路由
router.py聚合业务路由
user.py
project.py
project_member.py
task.py
projects.py
tasks.py
base.py声明式 Base
engine.pyEngine
session.pySession 工厂
projects.py
tasks.py
errors.py业务异常
main.pyFastAPI 应用入口
alembic.ini
pyproject.toml
目录图省略了各个 Python 包中的 __init__.py。db 也不是所有数据库业务的容器:Engine、Session、ORM 模型和查询与持久化直接相关;权限流程、编号规则和跨实体操作仍然属于 services。
一次创建请求会按下面的顺序执行:
create-task-flow.txt
1POST /api/v1/projects/12/tasks2-> tasks.py 校验路径参数和请求体3-> TaskService 检查成员身份并开启事务4-> ProjectRepository 查询成员关系、预留任务编号5-> TaskRepository 写入任务并 flush6-> TaskService 结束事务并提交7-> tasks.py 转换为 TaskRead8-> FastAPI 返回 201 JSON
这条路径可以帮助我们判断代码应该放在哪里。如果某段代码需要读取 Path 或设置 HTTP 状态码,它属于 Route;如果它要协调多个查询并决定一起提交或回滚,它属于 Service。
正在验证登录状态
请稍候,验证完成后将继续显示文章内容