创建时间: 2026-09-01最后更新: 2026-09-01

1. 分层解决什么问题

在只有一两个接口时,直接在路由函数中执行 select() 很容易理解。随着项目逐渐出现用户、项目、成员、任务和权限规则,路由如果同时负责参数校验、权限判断、事务控制和数据库查询,很快就会变得难以修改和测试。

分层不是为了增加文件数量,而是让不同类型的变化停留在各自的位置:

层主要职责不应该负责
RouteHTTP 路径、参数、依赖和状态码拼接数据库查询、决定事务提交
Schema请求校验与响应结构表关系、数据库迁移
Service业务规则、权限检查和事务边界依赖 Request、HTTPException
Repository查询与持久化操作HTTP 响应、完整业务流程
Model表、列、约束和 ORM 关系映射接口输入输出

以“创建任务”为例,HTTP 层需要知道接口是 POST /api/v1/projects/{project_id}/tasks,成功时返回 201;业务层需要保证当前用户是项目成员,并且任务编号在项目内唯一;持久化层则负责把这些要求转换成 SQLAlchemy 查询。

这些职责之间只有一个主要依赖方向:

dependency-direction.txt
1
HTTP 请求
2
-> Route + Schema
3
-> Service
4
-> Repository
5
-> AsyncSession + Model
6
-> 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
1
POST /api/v1/projects/12/tasks
2
-> tasks.py 校验路径参数和请求体
3
-> TaskService 检查成员身份并开启事务
4
-> ProjectRepository 查询成员关系、预留任务编号
5
-> TaskRepository 写入任务并 flush
6
-> TaskService 结束事务并提交
7
-> tasks.py 转换为 TaskRead
8
-> FastAPI 返回 201 JSON

这条路径可以帮助我们判断代码应该放在哪里。如果某段代码需要读取 Path 或设置 HTTP 状态码,它属于 Route;如果它要协调多个查询并决定一起提交或回滚,它属于 Service。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容