752 lines
21 KiB
Markdown
752 lines
21 KiB
Markdown
|
|
# 后端项目架构设计
|
|||
|
|
|
|||
|
|
::: tip 🎯 核心问题
|
|||
|
|
**API 越写越多,代码越来越乱,如何设计一个清晰、可维护的后端项目结构?** 这就像问:你是把所有工具都扔进一个抽屉,还是按功能分类整理?好的项目架构能让团队协作更高效,让系统扩展更轻松。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 为什么要关注后端项目架构?
|
|||
|
|
|
|||
|
|
### 1.1 从小脚本到大系统的演变
|
|||
|
|
|
|||
|
|
很多初学者刚开始写后端时,代码结构非常简单:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# app.py - 所有代码在一个文件
|
|||
|
|
from flask import Flask, request, jsonify
|
|||
|
|
import sqlite3
|
|||
|
|
|
|||
|
|
app = Flask(__name__)
|
|||
|
|
|
|||
|
|
@app.route('/users', methods=['GET'])
|
|||
|
|
def get_users():
|
|||
|
|
conn = sqlite3.connect('db.sqlite')
|
|||
|
|
users = conn.execute('SELECT * FROM users').fetchall()
|
|||
|
|
return jsonify(users)
|
|||
|
|
|
|||
|
|
@app.route('/users', methods=['POST'])
|
|||
|
|
def create_user():
|
|||
|
|
data = request.json
|
|||
|
|
conn = sqlite3.connect('db.sqlite')
|
|||
|
|
conn.execute('INSERT INTO users (name, email) VALUES (?, ?)',
|
|||
|
|
(data['name'], data['email']))
|
|||
|
|
conn.commit()
|
|||
|
|
return jsonify({'message': 'User created'})
|
|||
|
|
|
|||
|
|
# 还有订单、商品、支付...所有接口都在这个文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
几百行代码搞定一切,简单直接。但随着业务发展,问题开始出现:
|
|||
|
|
|
|||
|
|
- **接口多了**:一个文件几千行,找代码像"考古"
|
|||
|
|
- **逻辑复杂了**:业务规则散落在各处,修改容易遗漏
|
|||
|
|
- **数据库操作重复**:到处写 SQL,改表结构要改几十处
|
|||
|
|
- **测试困难**:代码耦合严重,单元测试难以编写
|
|||
|
|
|
|||
|
|
**问题的本质**:没有"章法",所有的逻辑都堆在一起,就像把所有的工具、零件、说明书都扔进一个抽屉。
|
|||
|
|
|
|||
|
|
### 1.2 好的架构像整理好的车间
|
|||
|
|
|
|||
|
|
想象一个整理好的工厂车间:
|
|||
|
|
|
|||
|
|
| 区域 | 功能 | 特点 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| **原料区** | 存放原材料 | 分类摆放,标签清晰 |
|
|||
|
|
| **加工区** | 生产加工 | 流水线作业,工序明确 |
|
|||
|
|
| **质检区** | 质量检查 | 统一标准,严格把关 |
|
|||
|
|
| **成品区** | 存放成品 | 整齐有序,易于出库 |
|
|||
|
|
| **工具室** | 存放工具 | 按需借用,用完归还 |
|
|||
|
|
|
|||
|
|
**好的后端架构**就是把代码也这样组织:每一层只关心自己的职责,数据像流水一样在各层之间传递。
|
|||
|
|
|
|||
|
|
::: tip 💡 通俗比喻:餐厅后厨的组织
|
|||
|
|
把后端系统想象成一家餐厅的后厨:
|
|||
|
|
|
|||
|
|
- **`controllers/`(出餐口)** = 服务员接单:接收订单、核对信息、上菜
|
|||
|
|
- **`services/`(厨师团队)** = 厨师做菜:按照菜谱加工、协调各工序
|
|||
|
|
- **`repositories/`(仓库管理)** = 仓管取料:从仓库取食材、记录库存
|
|||
|
|
- **`models/`(菜谱标准)** = 菜谱定义:宫保鸡丁需要什么料、什么口味
|
|||
|
|
- **`utils/`(工具柜)** = 厨具存放:刀、勺、秤等通用工具
|
|||
|
|
|
|||
|
|
**关键点**:每个角色职责明确,不会越界。服务员不会自己炒菜,厨师不会擅自改菜谱。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 经典分层架构详解
|
|||
|
|
|
|||
|
|
### 2.1 四层架构(Controller-Service-Repository-Model)
|
|||
|
|
|
|||
|
|
最经典的后端分层架构如下:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
my-backend-project/
|
|||
|
|
├── src/
|
|||
|
|
│ ├── controllers/ # 控制器层(Controller)
|
|||
|
|
│ │ ├── userController.js
|
|||
|
|
│ │ ├── orderController.js
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── services/ # 业务逻辑层(Service)
|
|||
|
|
│ │ ├── userService.js
|
|||
|
|
│ │ ├── orderService.js
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── repositories/ # 数据访问层(Repository/DAO)
|
|||
|
|
│ │ ├── userRepository.js
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── models/ # 数据模型层(Model/Entity)
|
|||
|
|
│ │ ├── user.js
|
|||
|
|
│ │ ├── order.js
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── middlewares/ # 中间件
|
|||
|
|
│ │ ├── auth.js
|
|||
|
|
│ │ ├── errorHandler.js
|
|||
|
|
│ │ └── validator.js
|
|||
|
|
│ ├── utils/ # 工具函数
|
|||
|
|
│ │ ├── logger.js
|
|||
|
|
│ │ ├── response.js
|
|||
|
|
│ │ └── validator.js
|
|||
|
|
│ ├── config/ # 配置文件
|
|||
|
|
│ │ ├── database.js
|
|||
|
|
│ │ ├── redis.js
|
|||
|
|
│ │ └── index.js
|
|||
|
|
│ ├── routes/ # 路由定义
|
|||
|
|
│ │ ├── userRoutes.js
|
|||
|
|
│ │ ├── index.js
|
|||
|
|
│ │ └── api.js
|
|||
|
|
│ ├── jobs/ 或 workers/ # 定时任务/后台任务
|
|||
|
|
│ │ └── emailWorker.js
|
|||
|
|
│ ├── events/ 或 subscribers/ # 事件监听
|
|||
|
|
│ │ └── userEvents.js
|
|||
|
|
│ └── app.js # 应用入口
|
|||
|
|
├── tests/ # 测试文件
|
|||
|
|
│ ├── unit/
|
|||
|
|
│ ├── integration/
|
|||
|
|
│ └── e2e/
|
|||
|
|
├── migrations/ # 数据库迁移
|
|||
|
|
├── seeds/ # 种子数据
|
|||
|
|
├── docs/ # 文档
|
|||
|
|
├── .env # 环境变量
|
|||
|
|
├── package.json
|
|||
|
|
└── README.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: tip 📊 从图解中你能看到什么?
|
|||
|
|
**分层逻辑**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────┐
|
|||
|
|
│ Controller 层(控制器层) │ ← 接待员:接收请求,返回响应
|
|||
|
|
│ - 接收 HTTP 请求 │
|
|||
|
|
│ - 参数校验、权限检查 │
|
|||
|
|
│ - 调用 Service │
|
|||
|
|
│ - 格式化响应 │
|
|||
|
|
├─────────────────────────────────────────┤
|
|||
|
|
│ Service 层(业务逻辑层) │ ← 厨师:处理核心业务
|
|||
|
|
│ - 业务逻辑编排 │
|
|||
|
|
│ - 事务管理 │
|
|||
|
|
│ - 调用 Repository │
|
|||
|
|
│ - 跨模块协调 │
|
|||
|
|
├─────────────────────────────────────────┤
|
|||
|
|
│ Repository 层(数据访问层) │ ← 仓管员:管理数据存取
|
|||
|
|
│ - 数据库操作 │
|
|||
|
|
│ - ORM 封装 │
|
|||
|
|
│ - 查询构建 │
|
|||
|
|
├─────────────────────────────────────────┤
|
|||
|
|
│ Model 层(数据模型层) │ ← 菜谱标准:定义数据结构
|
|||
|
|
│ - 实体定义(Entity) │
|
|||
|
|
│ - 类型定义 │
|
|||
|
|
│ - 业务规则验证 │
|
|||
|
|
└─────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**依赖方向**:
|
|||
|
|
```
|
|||
|
|
Controller → Service → Repository → Model
|
|||
|
|
↓
|
|||
|
|
Middleware / Utils
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
上层依赖下层,下层不依赖上层。Model 是核心,所有层都可能依赖它。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 2.2 各层职责详解
|
|||
|
|
|
|||
|
|
#### Controller 层:请求的"接待员"
|
|||
|
|
|
|||
|
|
Controller 是系统的"门面",负责接收 HTTP 请求并返回响应。
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 接收和解析请求参数
|
|||
|
|
- 调用相应的 Service 处理业务
|
|||
|
|
- 格式化响应数据
|
|||
|
|
- 处理 HTTP 相关逻辑(状态码、Header 等)
|
|||
|
|
|
|||
|
|
**不应该做的事**:
|
|||
|
|
- 直接操作数据库
|
|||
|
|
- 编写复杂业务逻辑
|
|||
|
|
- 处理事务
|
|||
|
|
|
|||
|
|
::: details 📝 Controller 代码示例(Node.js/Express)
|
|||
|
|
```javascript
|
|||
|
|
// controllers/userController.js
|
|||
|
|
const userService = require('../services/userService')
|
|||
|
|
const { success, error } = require('../utils/response')
|
|||
|
|
|
|||
|
|
class UserController {
|
|||
|
|
// 获取用户列表
|
|||
|
|
async list(req, res) {
|
|||
|
|
try {
|
|||
|
|
const { page = 1, limit = 10 } = req.query
|
|||
|
|
const users = await userService.getUsers({ page, limit })
|
|||
|
|
return success(res, users)
|
|||
|
|
} catch (err) {
|
|||
|
|
return error(res, err.message, 500)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取单个用户
|
|||
|
|
async getById(req, res) {
|
|||
|
|
try {
|
|||
|
|
const { id } = req.params
|
|||
|
|
const user = await userService.getUserById(id)
|
|||
|
|
if (!user) {
|
|||
|
|
return error(res, 'User not found', 404)
|
|||
|
|
}
|
|||
|
|
return success(res, user)
|
|||
|
|
} catch (err) {
|
|||
|
|
return error(res, err.message, 500)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建用户
|
|||
|
|
async create(req, res) {
|
|||
|
|
try {
|
|||
|
|
const userData = req.body
|
|||
|
|
const newUser = await userService.createUser(userData)
|
|||
|
|
return success(res, newUser, 201)
|
|||
|
|
} catch (err) {
|
|||
|
|
return error(res, err.message, 400)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = new UserController()
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
#### Service 层:业务的"厨师"
|
|||
|
|
|
|||
|
|
Service 是系统的"大脑",包含核心业务逻辑。
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 实现业务规则和流程
|
|||
|
|
- 协调多个 Repository 完成复杂操作
|
|||
|
|
- 管理事务
|
|||
|
|
- 数据转换和计算
|
|||
|
|
|
|||
|
|
**不应该做的事**:
|
|||
|
|
- 直接处理 HTTP 请求/响应
|
|||
|
|
- 直接操作数据库(通过 Repository)
|
|||
|
|
|
|||
|
|
::: details 📝 Service 代码示例
|
|||
|
|
```javascript
|
|||
|
|
// services/userService.js
|
|||
|
|
const userRepository = require('../repositories/userRepository')
|
|||
|
|
const orderRepository = require('../repositories/orderRepository')
|
|||
|
|
const emailService = require('./emailService')
|
|||
|
|
const { hashPassword } = require('../utils/crypto')
|
|||
|
|
|
|||
|
|
class UserService {
|
|||
|
|
// 获取用户列表
|
|||
|
|
async getUsers({ page, limit }) {
|
|||
|
|
const offset = (page - 1) * limit
|
|||
|
|
const [users, total] = await Promise.all([
|
|||
|
|
userRepository.findAll({ limit, offset }),
|
|||
|
|
userRepository.count()
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
data: users,
|
|||
|
|
pagination: {
|
|||
|
|
page,
|
|||
|
|
limit,
|
|||
|
|
total,
|
|||
|
|
totalPages: Math.ceil(total / limit)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取用户详情(包含订单信息)
|
|||
|
|
async getUserById(id) {
|
|||
|
|
const user = await userRepository.findById(id)
|
|||
|
|
if (!user) return null
|
|||
|
|
|
|||
|
|
// 获取用户订单统计
|
|||
|
|
const orderStats = await orderRepository.getStatsByUserId(id)
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
...user,
|
|||
|
|
orderStats
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建用户(包含事务和邮件通知)
|
|||
|
|
async createUser(userData) {
|
|||
|
|
// 检查邮箱是否已存在
|
|||
|
|
const existingUser = await userRepository.findByEmail(userData.email)
|
|||
|
|
if (existingUser) {
|
|||
|
|
throw new Error('Email already exists')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 密码加密
|
|||
|
|
const hashedPassword = await hashPassword(userData.password)
|
|||
|
|
|
|||
|
|
// 创建用户
|
|||
|
|
const newUser = await userRepository.create({
|
|||
|
|
...userData,
|
|||
|
|
password: hashedPassword
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 发送欢迎邮件(异步,不阻塞)
|
|||
|
|
emailService.sendWelcomeEmail(newUser.email).catch(console.error)
|
|||
|
|
|
|||
|
|
return newUser
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = new UserService()
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
#### Repository 层:数据的"仓管员"
|
|||
|
|
|
|||
|
|
Repository 负责所有与数据存储相关的操作。
|
|||
|
|
|
|||
|
|
**职责**:
|
|||
|
|
- 数据库的增删改查
|
|||
|
|
- ORM 映射
|
|||
|
|
- 查询优化
|
|||
|
|
|
|||
|
|
**不应该做的事**:
|
|||
|
|
- 包含业务逻辑
|
|||
|
|
- 处理事务(由 Service 控制)
|
|||
|
|
|
|||
|
|
::: details 📝 Repository 代码示例
|
|||
|
|
```javascript
|
|||
|
|
// repositories/userRepository.js
|
|||
|
|
const { User } = require('../models')
|
|||
|
|
|
|||
|
|
class UserRepository {
|
|||
|
|
// 查询所有用户
|
|||
|
|
async findAll({ limit, offset }) {
|
|||
|
|
return await User.findAll({
|
|||
|
|
limit,
|
|||
|
|
offset,
|
|||
|
|
attributes: { exclude: ['password'] } // 不返回密码
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据 ID 查询
|
|||
|
|
async findById(id) {
|
|||
|
|
return await User.findByPk(id, {
|
|||
|
|
attributes: { exclude: ['password'] }
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据邮箱查询
|
|||
|
|
async findByEmail(email) {
|
|||
|
|
return await User.findOne({ where: { email } })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建用户
|
|||
|
|
async create(data) {
|
|||
|
|
return await User.create(data)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新用户
|
|||
|
|
async update(id, data) {
|
|||
|
|
const user = await User.findByPk(id)
|
|||
|
|
if (!user) return null
|
|||
|
|
return await user.update(data)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除用户
|
|||
|
|
async delete(id) {
|
|||
|
|
const user = await User.findByPk(id)
|
|||
|
|
if (!user) return null
|
|||
|
|
await user.destroy()
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 统计用户数量
|
|||
|
|
async count() {
|
|||
|
|
return await User.count()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = new UserRepository()
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
#### Model 层:数据的"定义"
|
|||
|
|
|
|||
|
|
Model 定义数据结构和业务规则。
|
|||
|
|
|
|||
|
|
::: details 📝 Model 代码示例(Sequelize)
|
|||
|
|
```javascript
|
|||
|
|
// models/user.js
|
|||
|
|
const { DataTypes } = require('sequelize')
|
|||
|
|
const { sequelize } = require('../config/database')
|
|||
|
|
|
|||
|
|
const User = sequelize.define('User', {
|
|||
|
|
id: {
|
|||
|
|
type: DataTypes.UUID,
|
|||
|
|
defaultValue: DataTypes.UUIDV4,
|
|||
|
|
primaryKey: true
|
|||
|
|
},
|
|||
|
|
name: {
|
|||
|
|
type: DataTypes.STRING(100),
|
|||
|
|
allowNull: false,
|
|||
|
|
validate: {
|
|||
|
|
len: [2, 100]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
email: {
|
|||
|
|
type: DataTypes.STRING(255),
|
|||
|
|
allowNull: false,
|
|||
|
|
unique: true,
|
|||
|
|
validate: {
|
|||
|
|
isEmail: true
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
password: {
|
|||
|
|
type: DataTypes.STRING(255),
|
|||
|
|
allowNull: false
|
|||
|
|
},
|
|||
|
|
status: {
|
|||
|
|
type: DataTypes.ENUM('active', 'inactive', 'banned'),
|
|||
|
|
defaultValue: 'active'
|
|||
|
|
}
|
|||
|
|
}, {
|
|||
|
|
tableName: 'users',
|
|||
|
|
timestamps: true, // 自动添加 createdAt 和 updatedAt
|
|||
|
|
indexes: [
|
|||
|
|
{ fields: ['email'] },
|
|||
|
|
{ fields: ['status'] }
|
|||
|
|
]
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
module.exports = User
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 其他重要目录
|
|||
|
|
|
|||
|
|
### 3.1 `middlewares/` 中间件
|
|||
|
|
|
|||
|
|
中间件是请求处理流程中的"过滤器"。
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
middlewares/
|
|||
|
|
├── auth.js # 认证中间件
|
|||
|
|
├── errorHandler.js # 错误处理
|
|||
|
|
├── validator.js # 参数校验
|
|||
|
|
├── rateLimiter.js # 限流
|
|||
|
|
├── logger.js # 请求日志
|
|||
|
|
└── cors.js # 跨域处理
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
::: details 📝 中间件示例
|
|||
|
|
```javascript
|
|||
|
|
// middlewares/auth.js
|
|||
|
|
const jwt = require('jsonwebtoken')
|
|||
|
|
|
|||
|
|
const authMiddleware = (req, res, next) => {
|
|||
|
|
const token = req.headers.authorization?.split(' ')[1]
|
|||
|
|
|
|||
|
|
if (!token) {
|
|||
|
|
return res.status(401).json({ message: 'No token provided' })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET)
|
|||
|
|
req.user = decoded
|
|||
|
|
next()
|
|||
|
|
} catch (err) {
|
|||
|
|
return res.status(401).json({ message: 'Invalid token' })
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = authMiddleware
|
|||
|
|
```
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
### 3.2 `routes/` 路由
|
|||
|
|
|
|||
|
|
集中管理所有 API 路由。
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// routes/userRoutes.js
|
|||
|
|
const express = require('express')
|
|||
|
|
const router = express.Router()
|
|||
|
|
const userController = require('../controllers/userController')
|
|||
|
|
const authMiddleware = require('../middlewares/auth')
|
|||
|
|
|
|||
|
|
// 公开路由
|
|||
|
|
router.get('/', userController.list)
|
|||
|
|
router.get('/:id', userController.getById)
|
|||
|
|
|
|||
|
|
// 需要认证的路由
|
|||
|
|
router.post('/', authMiddleware, userController.create)
|
|||
|
|
router.put('/:id', authMiddleware, userController.update)
|
|||
|
|
router.delete('/:id', authMiddleware, userController.delete)
|
|||
|
|
|
|||
|
|
module.exports = router
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// routes/index.js
|
|||
|
|
const express = require('express')
|
|||
|
|
const router = express.Router()
|
|||
|
|
|
|||
|
|
router.use('/users', require('./userRoutes'))
|
|||
|
|
router.use('/orders', require('./orderRoutes'))
|
|||
|
|
router.use('/products', require('./productRoutes'))
|
|||
|
|
|
|||
|
|
module.exports = router
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 `config/` 配置
|
|||
|
|
|
|||
|
|
集中管理所有配置,支持多环境。
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// config/index.js
|
|||
|
|
const env = process.env.NODE_ENV || 'development'
|
|||
|
|
|
|||
|
|
const configs = {
|
|||
|
|
development: {
|
|||
|
|
port: 3000,
|
|||
|
|
database: {
|
|||
|
|
host: 'localhost',
|
|||
|
|
port: 5432,
|
|||
|
|
name: 'myapp_dev'
|
|||
|
|
},
|
|||
|
|
redis: {
|
|||
|
|
host: 'localhost',
|
|||
|
|
port: 6379
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
production: {
|
|||
|
|
port: process.env.PORT || 80,
|
|||
|
|
database: {
|
|||
|
|
host: process.env.DB_HOST,
|
|||
|
|
port: process.env.DB_PORT,
|
|||
|
|
name: process.env.DB_NAME
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = configs[env]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 `utils/` 工具
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
utils/
|
|||
|
|
├── logger.js # 日志工具
|
|||
|
|
├── response.js # 响应封装
|
|||
|
|
├── crypto.js # 加密解密
|
|||
|
|
├── date.js # 日期处理
|
|||
|
|
└── validator.js # 验证工具
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 按功能组织(Feature-based)
|
|||
|
|
|
|||
|
|
对于中大型项目,可以采用按功能组织的方式:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/
|
|||
|
|
├── features/
|
|||
|
|
│ ├── users/
|
|||
|
|
│ │ ├── users.controller.js
|
|||
|
|
│ │ ├── users.service.js
|
|||
|
|
│ │ ├── users.repository.js
|
|||
|
|
│ │ ├── users.model.js
|
|||
|
|
│ │ ├── users.routes.js
|
|||
|
|
│ │ ├── users.validator.js
|
|||
|
|
│ │ └── index.js # 统一导出
|
|||
|
|
│ ├── orders/
|
|||
|
|
│ │ ├── orders.controller.js
|
|||
|
|
│ │ ├── orders.service.js
|
|||
|
|
│ │ └── ...
|
|||
|
|
│ └── products/
|
|||
|
|
│ ├── products.controller.js
|
|||
|
|
│ └── ...
|
|||
|
|
├── shared/ # 共享资源
|
|||
|
|
│ ├── middlewares/
|
|||
|
|
│ ├── utils/
|
|||
|
|
│ └── config/
|
|||
|
|
└── app.js
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优点**:
|
|||
|
|
- 高内聚,一个功能的所有代码在一起
|
|||
|
|
- 便于团队协作,不同人负责不同 feature
|
|||
|
|
- 易于删除或重构
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 知名开源项目的架构参考
|
|||
|
|
|
|||
|
|
### 5.1 Express.js 官方示例
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
express-example/
|
|||
|
|
├── bin/ # 启动脚本
|
|||
|
|
├── public/ # 静态资源
|
|||
|
|
├── routes/ # 路由
|
|||
|
|
├── views/ # 视图模板
|
|||
|
|
├── app.js # 应用配置
|
|||
|
|
└── package.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:简单直接,适合小型项目。
|
|||
|
|
|
|||
|
|
### 5.2 NestJS(企业级 Node.js 框架)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
nestjs-project/
|
|||
|
|
├── src/
|
|||
|
|
│ ├── modules/ # 功能模块
|
|||
|
|
│ │ ├── users/
|
|||
|
|
│ │ │ ├── users.controller.ts
|
|||
|
|
│ │ │ ├── users.service.ts
|
|||
|
|
│ │ │ ├── users.module.ts
|
|||
|
|
│ │ │ └── dto/
|
|||
|
|
│ │ └── orders/
|
|||
|
|
│ ├── common/ # 共享模块
|
|||
|
|
│ ├── config/ # 配置
|
|||
|
|
│ └── main.ts # 入口
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 强制模块化结构
|
|||
|
|
- 内置依赖注入
|
|||
|
|
- 适合大型项目
|
|||
|
|
|
|||
|
|
### 5.3 Django(Python)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
django-project/
|
|||
|
|
├── project_name/ # 项目配置
|
|||
|
|
├── apps/
|
|||
|
|
│ ├── users/ # 用户应用
|
|||
|
|
│ │ ├── models.py
|
|||
|
|
│ │ ├── views.py
|
|||
|
|
│ │ ├── serializers.py
|
|||
|
|
│ │ └── urls.py
|
|||
|
|
│ └── orders/ # 订单应用
|
|||
|
|
├── templates/
|
|||
|
|
├── static/
|
|||
|
|
└── manage.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 约定优于配置
|
|||
|
|
- MTV(Model-Template-View)模式
|
|||
|
|
- 应用可复用
|
|||
|
|
|
|||
|
|
### 5.4 Spring Boot(Java)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
spring-boot-project/
|
|||
|
|
├── src/main/java/
|
|||
|
|
│ └── com/example/
|
|||
|
|
│ ├── controller/
|
|||
|
|
│ ├── service/
|
|||
|
|
│ ├── repository/
|
|||
|
|
│ ├── entity/
|
|||
|
|
│ ├── dto/
|
|||
|
|
│ ├── config/
|
|||
|
|
│ └── Application.java
|
|||
|
|
├── src/main/resources/
|
|||
|
|
│ ├── application.yml
|
|||
|
|
│ └── mapper/
|
|||
|
|
└── src/test/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- 严格的分层架构
|
|||
|
|
- 注解驱动开发
|
|||
|
|
- 强大的生态
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 架构设计原则与检查清单
|
|||
|
|
|
|||
|
|
### 6.1 核心原则
|
|||
|
|
|
|||
|
|
| 原则 | 说明 | 实践建议 |
|
|||
|
|
|------|------|----------|
|
|||
|
|
| **单一职责** | 一个模块只做一件事 | Controller 只处理 HTTP,Service 只处理业务 |
|
|||
|
|
| **依赖倒置** | 依赖抽象而非具体实现 | 使用接口/抽象类 |
|
|||
|
|
| **开闭原则** | 对扩展开放,对修改关闭 | 新增功能不修改原有代码 |
|
|||
|
|
| **DRY** | 不要重复自己 | 提取公共逻辑到 utils 或基类 |
|
|||
|
|
| **KISS** | 保持简单 | 不要过度设计 |
|
|||
|
|
|
|||
|
|
### 6.2 检查清单
|
|||
|
|
|
|||
|
|
**分层检查**:
|
|||
|
|
- [ ] Controller 是否只处理 HTTP 相关逻辑?
|
|||
|
|
- [ ] Service 是否包含核心业务逻辑?
|
|||
|
|
- [ ] Repository 是否只负责数据访问?
|
|||
|
|
- [ ] 层与层之间是否通过明确的接口交互?
|
|||
|
|
|
|||
|
|
**代码质量**:
|
|||
|
|
- [ ] 是否有统一的错误处理机制?
|
|||
|
|
- [ ] 是否使用环境变量管理配置?
|
|||
|
|
- [ ] 是否有日志记录?
|
|||
|
|
- [ ] 是否编写了单元测试?
|
|||
|
|
|
|||
|
|
**安全**:
|
|||
|
|
- [ ] 敏感配置是否放入环境变量?
|
|||
|
|
- [ ] 是否有输入验证?
|
|||
|
|
- [ ] 是否有认证和授权?
|
|||
|
|
- [ ] 密码是否加密存储?
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 总结
|
|||
|
|
|
|||
|
|
::: tip 💡 核心思想
|
|||
|
|
好的后端架构应该像一家组织良好的餐厅:
|
|||
|
|
|
|||
|
|
- **分工明确**:每个角色知道自己的职责
|
|||
|
|
- **流程清晰**:数据像流水一样在各层之间传递
|
|||
|
|
- **易于扩展**:新增功能不会破坏现有结构
|
|||
|
|
- **便于测试**:各层可以独立测试
|
|||
|
|
|
|||
|
|
**记住这几点**:
|
|||
|
|
1. **分层是手段,不是目的**:不要为了分层而分层
|
|||
|
|
2. **按功能组织**:中大型项目推荐 Feature-based
|
|||
|
|
3. **统一约定**:命名、结构、错误处理保持一致
|
|||
|
|
4. **持续重构**:定期审视架构,及时调整
|
|||
|
|
|
|||
|
|
**最终目标**:让代码像整理好的车间一样,想找什么立刻能找到,新功能容易添加,旧代码容易维护。
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 参考资源
|
|||
|
|
|
|||
|
|
- [NestJS 文档](https://docs.nestjs.com/)
|
|||
|
|
- [Express 最佳实践](https://expressjs.com/en/advanced/best-practice-security.html)
|
|||
|
|
- [Bulletproof Node.js](https://github.com/santiq/bulletproof-nodejs)
|
|||
|
|
- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|