
专栏AI 全栈开发05技术栈定下来以后真正打开 IDE 时你面对的不是架构图而是一个空文件夹。这一篇不追求“生产级目录看起来多专业”而是从 5 个文件开始先让每个东西有明确归属再随着项目变复杂逐步扩展。一、目录结构解决的不是“好不好看”而是以后去哪找代码刚建项目时所有文件放根目录也能跑。真正的问题通常在几周后才出现API 路由和模型调用混在一起迁移脚本不知道放哪Docker 配置散落谁都不敢删“final2.py”。所以目录结构最实用的目标只有两个• 看到一个文件名大致知道它应该属于哪类职责。• 想修改一类功能时知道主要应该去哪个目录找。只要这两点做到了目录就已经在帮你而不是在给项目增加仪式感。二、第一天不要先建 6 个空目录图 1 项目目录应该跟着真实职责生长不需要第一天把“目标态”全部建出来按照前面专栏的路线第一版只有 Frontend 和 Backend那么目录完全可以先这样app/ ├── README.md ├── .gitignore ├── .env.example ├── frontend/ └── backend/这已经足够建立最重要的边界浏览器侧代码进 frontend可信服务端代码进 backend。等真的有数据库迁移、部署配置、运维脚本和架构文档再把 scripts、infra、docs 加进来。三、为什么前端和后端建议分目录因为它们本来就是两套运行环境Frontend 依赖 Node / 浏览器生态Backend 依赖 Python 和服务端库构建、测试、环境变量也不一样。分开以后一个非常简单的规则就成立了frontend/ 不直接 import backend/ 的 Python 代码backend/ 也不去读 frontend/src 里的业务实现。两边通过 HTTP / Streaming API 等契约通信。如果以后想共享类型也更推荐通过 OpenAPI 生成客户端、共享 Schema 包等明确机制而不是相对路径跨目录乱 import。四、Monorepo 是一种方便的选择不是“唯一最优”对一个中小团队、同一个产品、前后端经常一起改的项目把 frontend 和 backend 放在同一个 Git 仓库里确实很方便一个 PR 能同时改接口和页面CI 也能一起跑。但这不代表 Monorepo 对所有组织都是最优。大型组织、独立发布节奏、不同权限边界、已有多仓库平台都可能有充分理由拆仓。本专栏使用 Monorepo是为了学习和项目演进更连贯。真正要记的是“职责边界”不是“所有 AI 项目必须一个仓库”。五、项目长大以后各目录分别应该管什么图 2 好目录的核心是边界不同职责各有归属跨边界通过契约连接frontend/浏览器里运行的产品代码页面路由、组件、Hooks、Streaming 客户端、上传 UI、前端状态都在这里。frontend/ ├── src/ │ ├── app/ # Next.js 页面 / 路由 │ ├── components/ # 可复用 UI │ ├── hooks/ # useChat 等 │ └── lib/ # API client、stream parser ├── public/ ├── package.json └── tsconfig.jsonbackend/可信服务端的业务代码路由只负责 HTTP 边界真正的模型调用、RAG、文件服务等放到 services数据库访问集中到 repositories配置和安全公共代码放 core。backend/ ├── app/ │ ├── main.py │ ├── api/ # 路由层 │ ├── services/ # 业务 / AI 编排 │ ├── repositories/ # DB / Cache 数据访问 │ ├── schemas/ # 请求 / 响应模型 │ └── core/ # config / auth / logging ├── tests/ └── pyproject.toml这里的分层也不是硬性教条。项目只有两三个接口时services 和 repositories 可以很轻不要为了目录完整把一行函数拆成五层。scripts/有明确生命周期的“动作”数据库初始化、一次性数据回填、导入测试数据、迁移辅助脚本等可以放在 scripts。关键不是“脚本必须一次性”而是它们不是在线请求路径里的业务模块。能重复执行的运维脚本也完全可以放这里只要命名清楚、行为可控。infra/部署与基础设施定义Docker Compose、Terraform、Kubernetes manifests、反向代理配置等可以放 infra。但 Dockerfile 放在哪里没有唯一答案。很多项目会把 frontend/Dockerfile、backend/Dockerfile 跟应用源码放在一起因为构建上下文更直观也有团队集中管理。选择一种规则并保持一致即可。docs/记录代码解释不了的“为什么”README 解决“怎么跑”docs/ 更适合保存架构总览、复杂流程、API 约定和 ADR。ADR 最有价值的不是记录“用了 PostgreSQL”而是记录“为什么当时选择 PostgreSQL、考虑过什么替代方案、什么时候重新评估”。六、根目录应该放什么根目录是整个仓库的入口适合放跨前后端都需要知道的契约文件。文件作用注意README.md启动、测试、目录导航新同事先看这里.gitignore忽略构建产物、虚拟环境、本地 Secret不要把 .env 提交.env.example示例配置 Key 与说明只放占位值不放真实 Secretcompose.yaml按需本地统一启动多个依赖服务项目不需要容器时可暂时没有Makefile / task runner按需统一常用开发命令不是必需品.env.example 不是生产配置的“唯一真相”。它更像开发者能看到的配置说明和模板真正的 staging / prod Secret 应由部署平台或 Secret Manager 管理。七、dev / staging / prod不要追求“环境长得一模一样”图 3 环境应该共享代码和契约但基础设施规模与实现可以不同原稿要求三套环境从第一天都用同一套 docker-compose这个说法太死。更实际的原则是• 应用代码尽量同源不通过改源码切换环境。• 配置 Key 和接口契约尽量一致Value 由环境注入。• 数据库迁移流程、安全规则、关键依赖版本要可重复。• dev 可以用本地依赖prod 可以用托管数据库或 Kubernetes基础设施不必逐字相同。Staging 的目标是尽可能验证生产关键行为但是否值得维护完整 staging也取决于产品规模和团队成本。小项目未必第一天就需要三套完整环境。八、一个更适合本专栏继续扩展的目录到真正加入数据库、文件、Worker 和部署后可以长成下面这样app/ ├── README.md ├── .gitignore ├── .env.example ├── frontend/ │ ├── src/ │ │ ├── app/ │ │ ├── components/ │ │ ├── hooks/ │ │ └── lib/ │ └── package.json ├── backend/ │ ├── app/ │ │ ├── api/ │ │ ├── services/ │ │ ├── repositories/ │ │ ├── schemas/ │ │ └── core/ │ ├── tests/ │ └── pyproject.toml ├── worker/ # 出现独立长任务进程后再加 ├── scripts/ ├── infra/ └── docs/这里特意没有单独建 docker/。Dockerfile 是否跟应用放一起、是否集中到 infra属于团队约定不值得在第 05 篇把它说成“正确答案”。九、几个最容易把目录越设计越复杂的误区• 误区 1目录越多越专业。空目录只会增加认知负担职责真的出现再建。• 误区 2所有后端代码都拆 Router / Service / Repository。分层是为了隔离复杂度不是为了给三行 CRUD 增加模板。• 误区 3Monorepo 是所有团队的最优解。它适合本专栏和很多中小团队但不是组织架构定律。• 误区 4dev、staging、prod 必须用完全相同的部署方案。应该一致的是行为和契约不是机器数量或云产品。• 误区 5.env.example 管理全部环境变量值。它只能进仓库做模板真实 Secret 不应该进入 Git。• 误区 6为了共享代码让前端直接 import 后端目录。跨运行时边界最好走 API / Schema 等明确契约。十、目录什么时候应该重构目录结构不需要追求“三年不动”。当下面情况开始频繁出现就说明边界需要调整• 一个目录里出现几十个职责完全不同的文件找代码越来越慢。• 同一类逻辑在多个目录重复出现。• 每次改一个模块都必须同时修改很多不相关目录。• 部署、测试或权限边界已经跟当前目录划分不一致。好的目录不是永远不变而是变化有原因、有迁移路径不是每周凭感觉换一次。十一、这一篇只需要记住一个原则目录结构的价值不是提前预测三年后的所有文件而是让“现在已经存在的职责”有稳定边界并且给下一阶段的复杂度留出自然生长的位置。十二、下一篇下一篇 06《Browser 到 Server一次请求到底发生了什么》会从文件结构进入运行时用户点下发送以后请求怎样从浏览器到 FastAPI再怎样把响应送回来。到那时frontend 和 backend 之间那条“HTTP 线”会真正变得透明。