科学的 Express 项目结构应该遵循清晰、可扩展、易于维护的原则。随着项目规模的扩大,合理的文件和模块组织将有助于团队协作和开发效率。通常我们采用分层架构,将项目的不同部分进行合理拆分,例如路由、控制器、模型、服务和中间件等。
my-express-app/
├── config/ # 配置相关的文件(如数据库配置、环境变量等)
│ └── db.js # 数据库配置文件
├── controllers/ # 控制器层,处理业务逻辑
│ └── userController.js # 用户相关控制器
├── middlewares/ # 中间件
│ └── authMiddleware.js # 认证中间件
├── models/ # 数据模型,通常用于与数据库交互
│ └── userModel.js # 用户模型
├── routes/ # 路由层,定义 API 路由
│ └── userRoutes.js # 用户相关的路由
├── services/ # 服务层,封装复杂业务逻辑和数据库操作
│ └── userService.js # 用户服务
├── utils/ # 工具库,存放通用的工具函数
│ └── logger.js # 日志工具
├── validations/ # 数据验证
│ └── userValidation.js # 用户数据验证逻辑
├── app.js # 应用入口,初始化 Express 应用
├── server.js # 启动文件,启动 Express 服务
├── .env # 环境变量文件
├── package.json # 项目配置文件
└── README.md # 项目说明文档
各部分功能说明
-
config/: 存放应用的配置文件,例如数据库连接、环境变量、全局设置等。这个目录可以帮助集中管理所有的配置。
1.1 db.js: 例如 MongoDB 连接配置,可以在这里实现数据库连接的逻辑。
-
controllers/: 控制器层,处理接收到的请求,调用服务层的逻辑并返回响应。控制器负责处理路由请求和响应,不直接操作数据库。
2.1 userController.js: 处理用户相关的请求,如新增用户、获取用户等。
-
middlewares/: 自定义的中间件放在这里,例如认证、日志记录、错误处理等。
3.1 authMiddleware.js: 认证中间件,检查用户是否登录。
-
models/: 定义数据模型,通常与数据库交互。每个模型对应数据库中的一张表或集合(例如,使用 Mongoose 定义 MongoDB 的模型)。
4.1 userModel.js: 定义用户的数据库模型。
-
routes/: 路由定义文件,每个路由对应一个控制器。路由用于将特定的 HTTP 请求映射到控制器中的处理函数。
5.1 userRoutes.js: 定义用户相关的 API 路由,如 /api/users。
-
services/: 服务层,封装复杂的业务逻辑或数据库操作。这使得业务逻辑从控制器中解耦,控制器只需要调用服务层的方法。
6.1 userService.js: 用户服务,处理与用户数据相关的逻辑(如数据库 CRUD 操作)。
-
utils/: 存放通用的工具函数或第三方库的封装。可以是日志工具、常用的辅助函数等。
7.1 logger.js: 日志工具,用于输出日志。
-
validations/: 数据验证逻辑,通常在控制器层使用以确保请求数据的合法性。
8.1 userValidation.js: 用户数据的验证逻辑,例如检查 email、密码等字段是否符合要求。
-
app.js: Express 应用的入口,通常在这里初始化 Express 以及加载中间件、路由等。
-
server.js: 应用的启动文件,通过它启动 Express 服务。
-
.env: 存储环境变量,例如数据库连接字符串、JWT 秘钥等,避免将敏感信息直接硬编码到项目中。
代码示例
1. app.js(Express 初始化)
const express = require('express');
const userRoutes = require('./routes/userRoutes');
const authMiddleware = require('./middlewares/authMiddleware');
const app = express();
// 中间件
app.use(express.json()); // 解析 JSON 请求体
// 路由
app.use('/api/users', userRoutes); // 用户相关路由
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
module.exports = app;
2. server.js(启动服务器)
const app = require('./app');
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
3. models/userModel.js(定义用户模型)
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
const User = mongoose.model('User', userSchema);
module.exports = User;
4. controllers/userController.js(用户控制器)
const userService = require('../services/userService');
const addUser = async (req, res) => {
try {
const { name, email, password } = req.body;
const newUser = await userService.createUser({ name, email, password });
res.status(201).json(newUser);
} catch (error) {
res.status(500).json({ message: 'Failed to create user' });
}
};
module.exports = { addUser };
5. routes/userRoutes.js(定义用户路由)
const express = require('express');
const { addUser } = require('../controllers/userController');
const router = express.Router();
router.post('/', addUser);
module.exports = router;
6. services/userService.js(用户服务)
const User = require('../models/userModel');
const createUser = async (userData) => {
const user = new User(userData);
return await user.save();
};
module.exports = { createUser };
项目结构的好处
- 分离关注点:每一层都有自己的职责(如控制器处理 HTTP 请求,服务层封装业务逻辑,模型处理数据库交互),便于维护和扩展。
- 可扩展性:随着项目的增长,可以轻松添加更多的路由、控制器或服务层逻辑。
- 清晰的目录结构:使项目结构直观,便于其他开发者理解和协作。
- 代码复用:通过将通用功能(如中间件、工具函数)独立出来,可以在项目的各个部分中重用。