一、为什么需要分层架构
想象一下你正在装修房子。如果把水电管道、瓷砖、家具全都混在一起施工,不仅后期维护困难,连换个灯泡都可能要拆墙。Node.js应用开发也是同样的道理,好的分层设计能让你的代码像精装房一样模块清晰。
分层模式的核心价值在于:
- 关注点分离:每层只处理特定职责
- 降低耦合度:修改某层不会影响其他层
- 便于协作:不同开发者可以并行开发不同层次
- 可测试性:每层都可以单独进行单元测试
// 反面教材:所有逻辑混在一起的代码
app.post('/users', async (req, res) => {
// 1. 验证输入
if(!req.body.name) return res.status(400).send('Name required')
// 2. 数据库操作
const user = await db.query('INSERT INTO users...')
// 3. 发送响应
res.json({ id: user.insertId })
// 4. 记录日志
fs.appendFileSync('log.txt', `User created at ${new Date()}`)
// 5. 发送欢迎邮件
await mailer.sendWelcomeEmail(user.email)
})
二、经典三层架构实践
2.1 表现层(Presentation Layer)
处理HTTP请求和响应,就像餐厅的前台接待。我们使用Express框架作为技术栈示例:
// controllers/userController.js
const userService = require('../services/userService')
module.exports = {
async createUser(req, res, next) {
try {
// 1. 接收并校验参数
const { name, email } = req.body
if (!name || !email) {
return res.status(400).json({ error: 'Missing parameters' })
}
// 2. 调用服务层
const newUser = await userService.createUser(name, email)
// 3. 返回标准化响应
res.status(201).json({
data: newUser,
meta: { timestamp: new Date() }
})
} catch (err) {
next(err) // 4. 错误处理交给中间件
}
}
}
2.2 业务逻辑层(Service Layer)
这是系统的"大脑",处理核心业务逻辑:
// services/userService.js
const userRepository = require('../repositories/userRepository')
const mailService = require('./mailService')
module.exports = {
async createUser(name, email) {
// 1. 业务规则校验
if (name.length > 50) {
throw new Error('Name too long')
}
// 2. 调用仓储层
const user = await userRepository.create({ name, email })
// 3. 触发后续流程
await mailService.sendWelcomeEmail(email)
// 4. 返回处理结果
return {
id: user.id,
name: user.name,
initials: name[0].toUpperCase()
}
}
}
2.3 数据访问层(Repository Layer)
相当于系统的"记忆中枢",封装所有数据库操作。这里使用Sequelize作为ORM示例:
// repositories/userRepository.js
const { User } = require('../models')
module.exports = {
async create(userData) {
return await User.create(userData)
},
async findById(id) {
return await User.findByPk(id, {
attributes: ['id', 'name', 'email']
})
},
async update(id, updates) {
const user = await User.findByPk(id)
if (!user) throw new Error('User not found')
return await user.update(updates)
}
}
三、进阶分层技巧
3.1 DTO(数据传输对象)模式
在不同层之间传递数据时,使用专门的对象来封装:
// dtos/UserDTO.js
class UserDTO {
constructor(user) {
this.id = user.id
this.fullName = `${user.firstName} ${user.lastName}`
this.avatar = user.profileImage || '/default-avatar.png'
}
toJSON() {
return {
id: this.id,
name: this.fullName,
avatar: this.avatar
}
}
}
// 在控制器中使用
const user = await userService.getUser(id)
res.json(new UserDTO(user))
3.2 依赖注入实现解耦
使用构造函数注入依赖,方便测试和替换实现:
// services/paymentService.js
class PaymentService {
constructor(paymentGateway, logger) {
this.gateway = paymentGateway
this.logger = logger
}
async charge(amount, card) {
try {
const result = await this.gateway.charge(amount, card)
this.logger.info(`Charged ${amount} to card ${card.last4}`)
return result
} catch (err) {
this.logger.error('Payment failed', err)
throw err
}
}
}
// 使用时
const stripeGateway = require('./gateways/stripe')
const logger = require('./utils/logger')
const paymentService = new PaymentService(stripeGateway, logger)
四、实战中的注意事项
层间通信规范:
- 上层可以调用下层,但禁止反向调用
- 同级层之间避免直接依赖
- 跨层调用必须通过中间层传递
错误处理策略:
// middleware/errorHandler.js
module.exports = function(err, req, res, next) {
// 业务错误
if (err instanceof BusinessError) {
return res.status(400).json({
error: err.message,
code: err.code
})
}
// 系统错误
console.error(err)
res.status(500).json({
error: 'Internal Server Error',
requestId: req.id
})
}
- 目录结构建议:
src/
├── config/ # 配置文件
├── controllers/ # 控制器层
├── services/ # 业务逻辑层
├── repositories/ # 数据访问层
├── models/ # 数据模型
├── dtos/ # 数据传输对象
├── middleware/ # 中间件
├── utils/ # 工具函数
└── app.js # 应用入口
- 性能优化技巧:
- 在控制器层做参数校验,尽早失败
- 服务层实现业务缓存逻辑
- 仓储层处理批量操作和查询优化
分层架构就像给代码穿上分体式服装,既保持了整体协调性,又方便单独更换某个部件。刚开始可能会觉得多写几层代码有点麻烦,但当项目规模超过1万行代码后,你就会感谢当初的选择。记住,好的架构不是一次性设计出来的,而是在不断重构中逐步演化形成的。
评论