搭建多模块 Node 后端 目录到底怎么划分才不靠直觉

2026-09-11 114 浏览 0 评论 0 字

Node 后端目录结构划分没有唯一标准,但有稳定可靠的套路。本文基于职责分离原则,讲解 app、config、routes、controllers、services、models、middlewares 等层级的职责边界,教你如何把稳定基础设施与变动业务代码分开,让查找和重构都不靠直觉。

问题背景

刚开始写 Node 后端那会儿,我习惯把所有东西都塞进 app.js ,路由、数据库连接、业务逻辑全挤在一起。等接口一多,几千行代码的文件改起来痛不欲生 - 改一个字段要翻半天,同事接手更是一脸懵。很多人问:目录结构到底有没有标准答案?我的回答是:没有唯一标准,但有稳定可靠的套路。

原理说明

目录划分的原则其实是"按职责,不按个人喜好"。核心思路是把 稳定不变的基础设施 和 天天变动的业务代码 区分开,二者用清晰的边界隔开。常见的分层是:

  • app / server :应用的组装入口,只负责把中间件、路由挂上去
  • config :配置中心,负责读环境变量、加载配置文件
  • routes (或 controllers ):接收 HTTP 请求,只做参数校验和调用下层,不写业务
  • services :业务逻辑的归属地,可被复用、可被测试
  • models :数据模型的抽象,跟 ORM / 数据库打交道
  • middlewares :可复用的中间件
  • utils / helpers :纯函数工具,不依赖框架
  • tests :测试目录

这样的好处是:你要找一段跟订单相关的逻辑,顺着业务进 services ,永远不迷路。

示例代码

一个典型的多模块目录骨架:

my-server/
├── app.js                 # 应用组装入口
├── index.js               # 进程启动文件(读配置、监听端口)
├── config/
│   ├── index.js           # 加载配置
│   └── env.js             # 环境解析
├── routes/
│   ├── index.js           # 路由汇总
│   └── order.js           # 订单相关路由
├── controllers/
│   └── orderController.js
├── services/
│   └── orderService.js
├── middlewares/
│   ├── auth.js
│   └── errorHandler.js
├── models/
│   └── order.js
├── utils/
│   └── httpResponse.js
└── tests/
    └── order.test.js

index.js 里只需要做一件事 - 把 app 拉起来:

const app = require('./app');
const config = require('./config');

const port = config.port || 3000;
app.listen(port, () => {
  console.log(`server listening on ${port}`);
});

app.js 只做组装:

const express = require('express');
const routes = require('./routes');
const { auth, errorHandler } = require('./middlewares');

const app = express();
app.use(express.json());
app.use('/api', routes);   // 把路由挂到统一前缀
app.use(errorHandler);     // 最后兜住异常

module.exports = app;

常见坑与解决方案

  • 坑一:所有文件怼在一个 routes 里,几十个路由文件全挤在一起。解法:按业务域拆 routes/order.js 、 routes/user.js ,一个文件一个子路由。
  • 坑二:把业务逻辑写进路由回调里,导致无法单测、无法复用。解法:路由只做参数拿取和响应,业务一律下沉到 services 。
  • 坑三:工具函数散落各处,同一段逻辑复制了三遍。解法:抽到 utils/ ,并且保持纯函数、无副作用,方便复用。
  • 坑四:启动逻辑和组装逻辑不分家,一次环境就翻车。解法: index.js 只管跑起来, app.js 只管组装,测试时直接引入 app.js 不用真的监听端口。

小结

目录划分从来不是一次成型的事,随着项目长大会生长。但只要守住"职责分离 + 稳定与变动分开"这条主线,后面重构的成本就会低很多。下一篇我们聊聊接口规划,接口多了以后怎么避免越写越乱。

FAQ(常见问题)

Q:是不是所有项目都必须拆这么多层目录?

A:不一定。目录结构要匹配项目规模,小项目过度拆分反而增加导航成本。建议从 app、routes、services、models、utils 这几个核心层起步,随业务变复杂再逐步扩展,重点守住"职责分离"的主线。

Q:业务逻辑到底放 routes 还是 services?

A:放 services。routes 只负责接请求、校验参数、给响应,真正的业务逻辑放到 services,才能被路由、定时任务、消息队列等多处复用,也方便单独测试。

Q:稳定基础设施和业务代码为什么要分开存放?

A:基础设施(如 config、middlewares、utils)改动频率低,业务代码变化频繁。分开存放能让边界清晰,改业务时不会牵连基础设施,天然的风险面和不可控范围更小。


发布评论

发布评论前请先 登录。
0 评论
点赞
收藏

评论列表 0

暂无评论