Skip to content

6.6 代码分层与目录调整 ​

随着认证、评论和 AI 功能加入,找到一段代码、判断一次修改会影响哪里,开始变得更费力。本节借餐厅前厅、后厨和仓库的分工解释代码分层,再讨论博客什么时候值得拆目录,以及怎样在不改变现有功能的前提下逐步调整。


SpringBoot 三层架构的工程美学 ​

在企业级后端开发(尤其是 Java / Spring 生态)中,最经典、最经受得住数十年考验的设计范式莫过于 Controller-Service-DAO/Repository 三层架构。

我们用一个餐厅的运转流程来形象理解三层架构的设计要点:

💡 一、SpringBoot 三层架构的工程美学

餐厅角色深度对照: ​

  1. 表现层 —— Controller(前厅服务员):
    • 职责单一:只负责微笑迎宾、校验顾客菜单有没有漏填(参数校验与 DTO 转换)、收银与递送餐品(返回响应体与状态码);
    • 职责边界:服务员绝不能亲自跑到后厨切肉炒菜。如果 Controller 里写满了数据库查询与复杂计算,前厅就会乱成一锅粥。
  2. 业务逻辑层 —— Service(后厨主厨):
    • 职责:专注业务流程编排(比如发文章时先检查敏感词、再扣减配额、再调用 AI 异步生成导读);
    • 不问出处:主厨只管把菜做好,他不关心这份订单是来自堂食客人(Web 端)、外卖骑手(移动端 App)还是电话预定(第三方 API 调用)。
  3. 数据访问层 —— 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 行代码,变成了一个无所不能但谁也看不懂的巨无霸;
  • 致命反噬 —— 上下文超载与频繁幻觉:
    1. AI 每次想要修改其中一个微小功能,都必须把整个几千行的文件从头读到尾,白白消耗大量 Tokens,响应速度越来越慢;
    2. 超出模型注意力聚焦范围后,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                # 全局通用项目约定

治理后的四大质变提升: ​

  1. 职责更明确:按路由、业务、数据等职责组织文件,不用固定行数作为拆分目标;
  2. 便于定位修改:评论相关逻辑有明确位置后,可以先检查对应路由和业务模块;实际阅读量与耗时仍取决于依赖关系。
  3. 路由版本平滑演进:引入 api/v1/ 命名空间,未来新增 v2 时仍需维护旧接口与调用方的兼容关系;
  4. 前后端完全解耦:前端既可以维持当前的轻量级单文件 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. 权威官方规范与开源参考 ​


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. 目录治理实操六步法(照着做,今天就能重构你的项目) ​

  1. 先备份基线:跑一次 uv run pytest -q 全绿存档,作为重构的“安全垫”;
  2. 画依赖图:让 AI 用 CodeGraph codegraph_explore 输出当前模块调用关系,理清谁依赖谁;
  3. 从外到内拆:先建 backend/app/api/v1/(路由层)→ 再拆 services/(业务层)→ 再拆 models/、schemas/(数据契约层);
  4. 一次只动一层:每完成一层拆分立即跑测试,保持“始终绿灯”状态,避免大爆炸式重构;
  5. 让 AI 打辅助:把本章“架构守卫 Prompt”发给 Trae,让它按规范拆分,你只需在 Diff 视图审查;
  6. 沉淀资产:重构完成后同步更新 agents.md、.trae/rules/ 与 docs/,把新目录结构写进“项目规则”,让未来的 AI 都知道往哪放代码。

练习:选择一个业务模块,按本节方法分步调整目录。每一步都运行当前测试,并记录接口、数据库和静态资源是否保持原有行为。文中的历史测试数量仅供参考,以你当前运行结果为准。


拆目录之前,先判断需要解决什么 ​

整理书架的目的在于更容易找书,并不是把每本书放进单独一个盒子。代码分层也应减少理解和修改的成本;如果为了凑层数让一个简单操作在许多文件间跳转,维护反而更麻烦。

对当前博客,可以先选一个职责相对独立的模块,例如评论。把路由搬出入口文件,保持路径、参数、状态码和返回字段不变,再运行已有检查。确认这一步没有改变行为后,才继续抽取业务逻辑。

需要特别留意文件移动带来的环境变化:数据库相对路径、静态文件目录、配置文件加载位置、导入路径和启动命令都可能受影响。接口测试通过后,还要重启服务并打开页面,检查是否仍使用原来的数据库和资源。

需要改变的情况可先采用的调整
入口文件路由过多按业务拆路由模块
多个接口重复同一业务判断提取共享业务函数
外部模型调用难以替换或测试把调用封装在独立边界
数据查询重复且变化频繁再考虑独立的数据访问层

FastAPI 多文件应用指南提供了路由拆分方式,但没有要求所有项目使用同一种目录树。本节蓝图是参考方案,判断是否值得采用,应看修改范围是否更清楚、依赖是否更容易追踪,而不是文件是否低于某个行数。

重构完成后更新启动说明和项目规则,并写明哪些功能保持不变、哪些结构发生调整。这样下一次接手项目时,文档才能继续发挥作用。

分层重构要保持外部行为不变 ​

目录重构的验收对象首先是外部行为。文章接口的路径、请求字段、状态码和返回结构,页面的访问地址,数据库文件与静态资源位置,都应在动手前列成兼容清单。确实需要改变的部分,则单独写成迁移说明。

可以一次移动一个业务模块:先把评论路由迁出入口文件,运行评论及文章删除测试;再抽取业务逻辑,重复同一组检查;最后整理数据访问代码。小步推进能让失败对应到最近一次调整,也便于撤回。

重构后还要从项目说明中的原命令启动一次,确认运行目录没有改变数据库相对路径。随后打开页面完成一轮增删改查,并检查日志与错误响应。测试通过只证明覆盖到的行为未变,启动方式和资源路径仍需要实际验证。


本节回顾 ​

本章从已有博客出发,依次加入账号权限、互动功能和 AI 辅助写作。后续继续增加功能时,可以沿用“确认现状、约定范围、分步实现、检查结果”的顺序。

🚀 五、第六章本章回顾