4.2 项目架构与最佳实践


4.2 项目架构与最佳实践

本节摘要:项目长大后的第一个问题是"代码放哪"。本节给出 Next.js 项目的推荐目录结构(按功能域组织)、分层设计(UI/数据/服务边界)、环境配置与团队规范,并总结一套"可维护性"的最佳实践清单。

学习目标

阅读完本节,你应当能够:

  1. 按功能域组织目录结构
  2. 理解 UI、数据、服务三层的边界
  3. 管理环境与配置
  4. 制定团队协作规范
  5. 用"可维护性清单"自检项目

问题与直觉:目录结构决定"找代码的速度"

项目几个月后,最大的成本是找代码——"这个功能在哪改?"目录混乱的项目,找代码的时间远超写代码。好的结构让"按功能找文件"成为直觉。

直觉类比:目录结构像"文件柜的分类"——按"功能"分类(auth、posts、admin)比按"技术"分类(components、lib、pages 各一堆)更符合业务直觉。找"登录相关的代码"去 auth/ 目录,而不是在 components/ 里翻。

💡 关键直觉:按功能域(feature)组织,而不是按技术类型组织——app/ 里按路由分,components/ 里也按功能分(features/auth/、features/posts/)。同一功能的所有代码(组件、逻辑、样式)聚在一起,改一个功能只动一个目录。

核心原理:推荐目录结构

my-app/ ├── app/ # 路由层(文件系统路由) │ ├── layout.tsx │ ├── page.tsx │ ├── (auth)/ │ │ ├── login/page.tsx │ │ └── register/page.tsx │ ├── dashboard/page.tsx │ ├── posts/ │ │ ├── page.tsx # 列表 │ │ └── [slug]/page.tsx # 详情 │ └── api/ # API 路由 │ └── posts/route.ts ├── components/ # 组件层(按功能域) │ ├── ui/ # 基础 UI(Button、Card) │ ├── features/ # 功能组件 │ │ ├── auth/ # 认证相关组件 │ │ └── posts/ # 文章相关组件 │ └── layouts/ # 布局组件 ├── lib/ # 数据与服务层 │ ├── db.ts # 数据库客户端 │ ├── auth.ts # 认证配置 │ └── actions/ # Server Actions │ ├── auth-actions.ts │ └── post-actions.ts ├── store/ # 全局状态(按需) ├── types/ # 共享类型 ├── public/ # 静态资源 ├── prisma/ # 数据库模型 └── next.config.ts

工程实践要点:分层与规范

3.1 三层边界

3.1 三层边界

分层规则

  1. 路由层只做"组织":页面文件组合组件,不写复杂业务逻辑;
  2. 组件层只做"展示与交互":纯组件靠 props 驱动,不直接碰数据库;
  3. 数据层做"业务":Server Actions、数据获取、认证逻辑集中在 lib/;
  4. 依赖单向:上层依赖下层,禁止反向依赖。

3.2 Server Actions 的归置

// lib/actions/post-actions.ts —— 按功能域分文件 "use server"; import { prisma } from "@/lib/db"; import { revalidatePath } from "next/cache"; export async function createPost(formData: FormData) { ... } export async function deletePost(id: number) { ... }

规范:Actions 按功能域分文件、集中在 lib/actions/,页面 import 使用。

3.3 环境与配置规范

  • 环境变量集中在 .env*,用 lib/config.ts 统一读取;
  • 敏感值绝不硬编码、绝不加 NEXT_PUBLIC_ 暴露;
  • 配置按环境隔离(开发/预览/生产)。

3.4 团队协作规范

规范 做法
代码风格 ESLint + Prettier 强制
类型 TypeScript strict 模式
提交 约定式提交(feat/fix/docs)
分支 main 保护,PR 合并
CI lint + test + build 闸门
文档 README + 架构说明

常见误区与排查

误区 现象 正解
按技术类型堆文件 找代码难 按功能域组织
组件里直接查库 逻辑散落、难测试 数据层 lib/ 集中
循环导入 报错 保持依赖单向
Actions 散落各处 找不到、难维护 集中 lib/actions/
无 lint/类型检查 代码风格失控 CI 强制

动手演练:重构一个混乱项目

# 1. 按功能域建目录 mkdir -p components/features/{auth,posts} lib/actions store types # 2. 迁移组件 mv components/LoginForm.tsx components/features/auth/ mv components/PostCard.tsx components/features/posts/ # 3. 集中 Server Actions mv app/actions.ts lib/actions/post-actions.ts # 4. 统一数据访问 mv lib/db/client.ts lib/db.ts # 5. 用 lint + tsc 验证 npm run lint && npx tsc --noEmit

重构后:找"登录相关"去 components/features/auth/ + lib/actions/auth-actions.ts——每个功能的代码都在可预期的位置

重点提炼

  • 按功能域组织:app/ 按路由、components/features/ 按功能,改一个功能只动一个目录。
  • 三层边界:路由层(组织)、组件层(展示交互)、数据层(业务),依赖单向。
  • Actions 集中:lib/actions/ 按功能分文件。
  • 环境规范:.env 管理 + lib/config.ts 统一读取。
  • 团队规范:ESLint/TS strict/约定式提交/CI 闸门。
  • 可维护性:找代码快、改动影响小、依赖清晰。

深入理解:架构的演进与决策

项目结构演进路径

演进原则:不要一开始就设计"完美架构"——项目小的时候,结构简单最重要;当"找代码"开始变慢、改动影响面变大时,才分层重构。架构是长出来的,不是设计出来的

架构决策的三个问题

  1. 这段代码属于哪一层?(路由层/组件层/数据层)——答案明确,代码位置就明确;
  2. 谁依赖谁?(依赖单向向下)——方向乱 = 循环导入 + 隐性耦合;
  3. 改它影响谁?(影响面越小越好)——影响面大说明耦合高,需要拆分。

团队规范的落地顺序

规范三步走:先写约定(文档)→ 用工具固化(ESLint/Prettier)→ 进 CI 强制(不过不给合并)。靠自觉的规范会滑坡,工具强制的规范才持久

一句话:架构的检验标准很简单——新同事(或三个月后的你)能否在十分钟内找到"加一个功能"该改哪些文件。能,架构就合格。

常见问题速答

问:小项目也要按这个架构来吗?
不用。项目小时(几十个文件以内),简单的"app/ + components/ + lib/"就够了。架构随项目长大演进——过早分层是过度设计,找代码变慢时再分层。

问:Server Actions 放 lib/actions 还是页面目录?
建议集中放 lib/actions/ 按功能分文件(auth-actions、post-actions)。理由:Action 是"业务逻辑"而非"页面展示",集中便于复用与测试;页面只负责调用。

问:怎么避免循环导入?
保持依赖单向(路由层 → 组件层 → 数据层);扩展/工具放 lib/ 独立模块;页面不直接互相 import(用路由跳转代替)。

问:类型定义放哪?
共享类型放 types/ 目录;组件内私有类型放组件文件。Prisma 生成的类型(从 @prisma/client 导入)直接复用,不用重复定义。

问:重构的节奏?
每完成一个功能顺手整理;发现"放错位置"立即移动(小步提交);大重构用"先搭骨架、逐个迁移、每步可运行"推进。每次改动后应用都能跑


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U