主题
6.6 代码分层与目录调整
随着认证、评论和 AI 功能加入,找到一段代码、判断一次修改会影响哪里,开始变得更费力。本节借餐厅前厅、后厨和仓库的分工解释代码分层,再讨论博客什么时候值得拆目录,以及怎样在不改变现有功能的前提下逐步调整。
SpringBoot 三层架构的工程美学
在企业级后端开发(尤其是 Java / Spring 生态)中,最经典、最经受得住数十年考验的设计范式莫过于 Controller-Service-DAO/Repository 三层架构。
我们用一个餐厅的运转流程来形象理解三层架构的设计要点:
餐厅角色深度对照:
- 表现层 ——
Controller(前厅服务员):- 职责单一:只负责微笑迎宾、校验顾客菜单有没有漏填(参数校验与 DTO 转换)、收银与递送餐品(返回响应体与状态码);
- 职责边界:服务员绝不能亲自跑到后厨切肉炒菜。如果 Controller 里写满了数据库查询与复杂计算,前厅就会乱成一锅粥。
- 业务逻辑层 ——
Service(后厨主厨):- 职责:专注业务流程编排(比如发文章时先检查敏感词、再扣减配额、再调用 AI 异步生成导读);
- 不问出处:主厨只管把菜做好,他不关心这份订单是来自堂食客人(Web 端)、外卖骑手(移动端 App)还是电话预定(第三方 API 调用)。
- 数据访问层 ——
DAO / Repository(食材仓库管理员):- 专司存取:只负责按照主厨的指令去冷库(数据库)拿取或存放指定 ID 的食材;
- 不问用途:库管员绝不关心这块牛排是要做成全熟还是七分熟,保证存取高效、无脏数据即可。
🌟 软件工程第一性原理:三层架构的核心在于 关注点分离(Separation of Concerns) 与 单一职责原则(Single Responsibility Principle, SRP)。层与层之间通过清晰的接口契约通信,杜绝越俎代庖。
为什么要进行目录治理?
在刚才的博客开发中,为了让初学者在单屏内快速看清全貌,我们采用了相对扁平的目录(main.py、models.py、schemas.py、security.py、ai_service.py 全部放在工程根目录下)。
但一旦项目继续演进,这种扁平结构就会暴露出致命的痛点:
1. 痛点一:根目录“扁平大杂烩”,工程职责混乱
- 当模块从 1 个增加到 10 个(文章、用户、评论、点赞、AI、支付、搜索、通知、埋点...),根目录下会瞬间堆满上百个文件;
- 开发者很难一眼看出“哪些是给前端调用的接口路由,哪些是核心底层安全配置,哪些是纯粹的业务算法”,团队协作时极易产生 Git 代码合并冲突(Merge Conflicts)。
2. 痛点二:缺少分层约定时,代码容易集中在同一文件
在日常使用 AI 编程(无论是 Cursor、Trae、Claude Code 还是 OpenCode)时,所有开发者都会遇到一个非常经典的现象:
- AI 可能沿用现有文件结构继续追加:当你让 AI “新增一个点赞接口”时,如果项目没有强制分层规范,AI 可能把点赞的数据模型、Pydantic 校验、权限判断、数据库查询甚至前端 HTML 模板一口气全塞进
main.py末尾。 - “上帝文件(God File)”的诞生:不知不觉中,
main.py膨胀到了 3000~5000 行代码,变成了一个无所不能但谁也看不懂的巨无霸; - 致命反噬 —— 上下文超载与频繁幻觉:
- AI 每次想要修改其中一个微小功能,都必须把整个几千行的文件从头读到尾,白白消耗大量 Tokens,响应速度越来越慢;
- 超出模型注意力聚焦范围后,AI 极易在生成代码时误删掉文件其他区域的关键逻辑,导致“按下葫芦浮起瓢”,修一个 Bug 引入三个新 Bug。
3. 痛点三:软件工程的可测试性与并行开发
- 在扁平结构下,业务逻辑和数据库查询死死纠缠在路由函数里,想要对复杂的点赞防刷或批量回填算法写单元测试,必须每次都启动真实的数据库和 HTTP 服务;
- 治理后将业务抽离至
services/,我们可以轻松对业务逻辑进行纯粹的单元测试(Mock 掉底层网络与 IO),测试可以减少不必要的外部依赖,且不同工程师可以同时独立开发各自的 Service。
当前博客系统的目录治理重构建议方案
下面给出一种适合继续扩展博客的目录参考方案。它尚未表示配套项目已经完成重构,也不是 FastAPI 要求的唯一结构:
project_01_个人博客系统二次开发/
├── backend/ # 🐍 后端工程化主目录
│ ├── app/ # 核心应用源码
│ │ ├── api/ # 🚪 表现层 (Controller / Routers)
│ │ │ ├── v1/ # API 版本控制 (便于未来平滑升级)
│ │ │ │ ├── auth.py # 用户登录鉴权、Token 颁发
│ │ │ │ ├── posts.py # 文章增删改查、参数化分页
│ │ │ │ ├── comments.py # 楼层评论发表、权限删除
│ │ │ │ ├── likes.py # 点赞防刷、取消点赞
│ │ │ │ └── ai.py # AI 导读提炼、批量回填接口
│ │ │ └── api.py # 集中聚合注册所有子路由
│ │ ├── core/ # 🛡️ 核心基建与基础配置
│ │ │ ├── config.py # Pydantic BaseSettings 强类型配置
│ │ │ ├── security.py # Bcrypt 哈希算法、JWT 签发与权限守卫
│ │ │ └── database.py # SQLAlchemy 连接池引擎与 SessionLocal
│ │ ├── models/ # 🗄️ 数据持久层实体模型 (ORM Entities)
│ │ │ ├── user.py # User 数据表定义
│ │ │ ├── post.py # Post 数据表定义
│ │ │ ├── comment.py # Comment 数据表定义
│ │ │ └── like.py # Like 数据表定义 (含复合唯一索引)
│ │ ├── schemas/ # 📋 数据契约传输层 (Pydantic DTOs)
│ │ │ ├── user.py # 用户相关请求与响应 DTO
│ │ │ ├── post.py # 文章相关请求与分页 DTO
│ │ │ ├── comment.py # 评论入参与楼层出参 DTO
│ │ │ └── ai.py # AI 结构化输出校验 DTO
│ │ ├── services/ # 👨🍳 业务逻辑服务层 (Business Logic)
│ │ │ ├── post_service.py # 复杂文章分页过滤、批量聚合统计
│ │ │ ├── social_service.py # 幂等点赞计算、楼层排序与级联删除
│ │ │ └── ai_service.py # 大模型 OpenAI 客户端与 JSON 解析
│ │ └── main.py # 🚀 应用装配总入口 (Lifespan、CORS、中间件挂载)
│ ├── tests/ # 🧪 现代化自动化测试矩阵 (ATDD)
│ │ ├── conftest.py # 全局 TestClient Fixture 与内存测试库
│ │ ├── test_auth.py # 鉴权安全专项测试套件
│ │ ├── test_posts.py # 文章 CRUD 与分页专项测试
│ │ ├── test_social.py # 社交点赞防刷与楼层专项测试
│ │ └── test_ai.py # AI 导读与批量回填 Mock 测试
│ ├── pyproject.toml # uv 依赖管理声明
│ ├── uv.lock # 依赖锁版本
│ └── .env.example # 环境变量脱敏示例模版
│
├── frontend/ # 🎨 前端工程化目录
│ ├── public/ # 静态资源 (图标、Favicon)
│ └── index.html # 单文件高颜值玻璃拟态前端
│
├── docs/ # 📝 演进蓝图与设计计划 (计划先行留档)
├── reference/ # 📦 历史已验收规格归档区 (上下文物理隔离)
├── .trae/ # 🤖 Trae 专属智能体大脑与规则
│ ├── rules/
│ │ ├── backend.md # 后端开发红线与分层规范
│ │ ├── frontend.md # 前端样式与 Skills 调度规范
│ │ └── general.md # CodeGraph 先行与通用规范
│ └── skills/ # 专属技能工具包
└── agents.md / .traerules # 全局通用项目约定治理后的四大质变提升:
- 职责更明确:按路由、业务、数据等职责组织文件,不用固定行数作为拆分目标;
- 便于定位修改:评论相关逻辑有明确位置后,可以先检查对应路由和业务模块;实际阅读量与耗时仍取决于依赖关系。
- 路由版本平滑演进:引入
api/v1/命名空间,未来新增 v2 时仍需维护旧接口与调用方的兼容关系; - 前后端完全解耦:前端既可以维持当前的轻量级单文件
index.html,未来也可以平移重构成 Vue 3 / React / Next.js 独立工程。
目录治理延伸阅读:经典著作、权威规范与 AI Skills
如果想进一步了解分层与依赖设计,可以从下面的书籍和文档选择感兴趣的部分阅读:
1. 必读经典著作推荐
| 著作名称 | 核心作者 | 为什么强烈推荐? |
|---|---|---|
| 《代码整洁之道》(Clean Code) | Robert C. Martin (Uncle Bob) | 软件工程领域的“圣经”。深入讲解了函数短小、单一职责、有意义的命名与消除重复代码(DRY),是每一位优秀程序员的案头必备。 |
| 《整洁架构》(Clean Architecture) | Robert C. Martin (Uncle Bob) | 系统讲解了依赖倒置原则(Dependency Inversion Rule)与架构边界划分。教你如何让核心业务逻辑独立于 UI、框架、数据库和外部工具,构建具有超长生命周期的弹性系统。 |
| 《企业应用架构模式》(PoEAA) | Martin Fowler | 软件架构大师 Martin Fowler 的代表作。系统性梳理了三层架构、数据映射器(Data Mapper)、事务脚本(Transaction Script)与领域模型(Domain Model)的权威选型依据。 |
| 《领域驱动设计》(Domain-Driven Design) | Eric Evans | 复杂大型软件工程的武器。讲解如何将系统按业务领域(Domain)划分为限界上下文(Bounded Context),指导微服务与微模块的目录拆分。 |
2. 权威官方规范与开源参考
- FastAPI 官方全栈项目模板:fastapi/full-stack-fastapi-template (GitHub) —— 官方出品的参考架构,涵盖分层、Alembic 迁移、Docker 编排与安全基准;
- Martin Fowler 架构专栏:martinfowler.com/architecture/ —— 深入剖析分层架构(Layered Architecture)的演进与边界设计;
- FastAPI 复杂应用拆分官方指南:FastAPI Bigger Applications 指南 —— 官方教学如何使用
APIRouter进行多文件组织与依赖注入。
3. 接入 AI Agent 的目录治理 Skills 与实战 提示词模板
在日常 Vibe Coding 开发中,我们可以给 Trae Agent 注入专门的**“反上帝文件守卫”规则与“架构重构”**技能,让 AI 主动守护清晰的工程目录。
推荐给 AI 的守卫 提示词模板:
markdown
# 架构守卫与重构 Prompt 指令
角色:你是一位资深软件架构师,精通 Clean Architecture 与 FastAPI 三层架构。
任务:请审查当前项目代码并进行目录治理重构:
1. 严格遵守单一职责原则,禁止在单个文件中杂糅路由、业务逻辑与数据库查询;
2. 将 models、schemas、services、routers 独立拆解到对应分层子目录;
3. 根据职责和依赖拆分,不以固定行数为目标;
4. 重构过程中严格遵守 ATDD 守卫,确保既有 pytest 测试用例 100% 绿灯通过!架构选择与维护:SOLID 原则、前沿架构流派与“坏味道”检测
1. SOLID 五大设计原则(设计原则)
| 原则 | 全称 | 对应场景 | 在我们项目中的体现 |
|---|---|---|---|
| S | 单一职责原则 (SRP) | 一个厨师只负责一道菜,不身兼采购+洗碗+收银 | 路由(main.py)、模型(models.py)、校验(schemas.py)、安全(security.py)、AI(ai_service.py)各司其职 |
| O | 开闭原则 (OCP) | 餐厅加新菜只加菜单,不用重装修厨房 | 新增评论/点赞接口无需改动既有文章接口,向后兼容扩展 |
| L | 里氏替换原则 (LSP) | 换一个牌子的叉车,操作方式不能变 | 模型替换(如换 ORM)时,上层调用契约保持稳定 |
| I | 接口隔离原则 (ISP) | 顾客只需要一张菜单,而不是一本《餐厅经营大全》 | 按 LoginRequest / PostCreate / CommentCreate 拆分细粒度 DTO |
| D | 依赖倒置原则 (DIP) | 主厨依赖“菜谱规范”,而不是依赖某个特定供应商 | 路由依赖 Depends 抽象守卫,而不是在函数里硬编码鉴权逻辑 |
🧠 为什么 Vibe Coding 时代 SOLID 更重要? 因为 AI 会“忠实执行”你的代码结构暗示——你把文件拆得职责清晰,AI 就知道去哪找、往哪改;你把一堆逻辑糊在一个 5000 行的“上帝文件”里,AI 就会继续往里面疯狂追加,直到把项目糊成难以维护的代码。目录即契约,结构即规范。
2. 三层架构、六边形架构与洋葱架构的比较
- 三层架构(Controller-Service-DAO):我们本章的“主教练”。够用、直观、团队共识度高,是大多数中小型项目的起点;
- 六边形架构(Hexagonal / Ports & Adapters):把核心业务放在中心,通过端口连接数据库、UI 等外部适配器。这样可以降低业务逻辑对具体基础设施的依赖,但替换数据库或框架仍需要迁移和验证;
- 洋葱架构(Onion Architecture):由 Jeffrey Palermo 提出,强调依赖朝向核心。它与整洁架构有相近思想,但不是同一个名称。依赖方向永远“从外向内”——框架、数据库、UI 是最外层,可随时替换;核心实体与用例在最内层,永不依赖外部细节。
💡 演进路线建议:先熟练三层架构 → 再理解六边形 → 最后掌握 Clean Architecture。本教程采用三层 + 模块化目录,已经为未来的架构升级铺好了“换层不换芯”的路径。
3. “坏味道”自测清单:你的项目需要目录治理了吗?
| 检测信号 | 危险程度 | 应对建议 |
|---|---|---|
单个 .py 文件超过 500 行 | 🔴 高危 | 检查职责是否混杂,再决定拆分 |
同一文件里同时出现 @app.post、SQL 查询、前端 HTML | 🔴 高危 | 三层职责混装,按 api/services/models 拆分 |
| 新建一个功能要改 5+ 个不相关文件 | 🟡 中危 | 关注点耦合,考虑引入 Service 层收拢 |
| 测试用例需要启动完整数据库和 HTTP 服务 | 🟡 中危 | 业务逻辑抽离到 services 后可纯单元测试 |
| 代码复制粘贴现象频发 | 🟡 中危 | 提取公共函数/基类(DRY) |
一个文件里堆着 10+ 个 import 且互相无关联 | 🟢 观察 | 职责开始发散,规划拆分 |
4. 目录治理实操六步法(照着做,今天就能重构你的项目)
- 先备份基线:跑一次
uv run pytest -q全绿存档,作为重构的“安全垫”; - 画依赖图:让 AI 用 CodeGraph
codegraph_explore输出当前模块调用关系,理清谁依赖谁; - 从外到内拆:先建
backend/app/api/v1/(路由层)→ 再拆services/(业务层)→ 再拆models/、schemas/(数据契约层); - 一次只动一层:每完成一层拆分立即跑测试,保持“始终绿灯”状态,避免大爆炸式重构;
- 让 AI 打辅助:把本章“架构守卫 Prompt”发给 Trae,让它按规范拆分,你只需在 Diff 视图审查;
- 沉淀资产:重构完成后同步更新
agents.md、.trae/rules/与docs/,把新目录结构写进“项目规则”,让未来的 AI 都知道往哪放代码。
练习:选择一个业务模块,按本节方法分步调整目录。每一步都运行当前测试,并记录接口、数据库和静态资源是否保持原有行为。文中的历史测试数量仅供参考,以你当前运行结果为准。
拆目录之前,先判断需要解决什么
整理书架的目的在于更容易找书,并不是把每本书放进单独一个盒子。代码分层也应减少理解和修改的成本;如果为了凑层数让一个简单操作在许多文件间跳转,维护反而更麻烦。
对当前博客,可以先选一个职责相对独立的模块,例如评论。把路由搬出入口文件,保持路径、参数、状态码和返回字段不变,再运行已有检查。确认这一步没有改变行为后,才继续抽取业务逻辑。
需要特别留意文件移动带来的环境变化:数据库相对路径、静态文件目录、配置文件加载位置、导入路径和启动命令都可能受影响。接口测试通过后,还要重启服务并打开页面,检查是否仍使用原来的数据库和资源。
| 需要改变的情况 | 可先采用的调整 |
|---|---|
| 入口文件路由过多 | 按业务拆路由模块 |
| 多个接口重复同一业务判断 | 提取共享业务函数 |
| 外部模型调用难以替换或测试 | 把调用封装在独立边界 |
| 数据查询重复且变化频繁 | 再考虑独立的数据访问层 |
FastAPI 多文件应用指南提供了路由拆分方式,但没有要求所有项目使用同一种目录树。本节蓝图是参考方案,判断是否值得采用,应看修改范围是否更清楚、依赖是否更容易追踪,而不是文件是否低于某个行数。
重构完成后更新启动说明和项目规则,并写明哪些功能保持不变、哪些结构发生调整。这样下一次接手项目时,文档才能继续发挥作用。
分层重构要保持外部行为不变
目录重构的验收对象首先是外部行为。文章接口的路径、请求字段、状态码和返回结构,页面的访问地址,数据库文件与静态资源位置,都应在动手前列成兼容清单。确实需要改变的部分,则单独写成迁移说明。
可以一次移动一个业务模块:先把评论路由迁出入口文件,运行评论及文章删除测试;再抽取业务逻辑,重复同一组检查;最后整理数据访问代码。小步推进能让失败对应到最近一次调整,也便于撤回。
重构后还要从项目说明中的原命令启动一次,确认运行目录没有改变数据库相对路径。随后打开页面完成一轮增删改查,并检查日志与错误响应。测试通过只证明覆盖到的行为未变,启动方式和资源路径仍需要实际验证。
本节回顾
本章从已有博客出发,依次加入账号权限、互动功能和 AI 辅助写作。后续继续增加功能时,可以沿用“确认现状、约定范围、分步实现、检查结果”的顺序。