分层架构设计与模块划分
分层架构设计的基本概念
分层架构是一种常见的软件设计模式,它将系统划分为多个层次,每个层次负责特定的功能。在Koa2应用中,典型的分层架构通常包括:
- 表现层(Presentation Layer):处理HTTP请求和响应
- 业务逻辑层(Business Logic Layer):实现核心业务逻辑
- 数据访问层(Data Access Layer):与数据库交互
- 基础设施层(Infrastructure Layer):提供通用服务(如日志、配置等)
// 基础分层示例
app
.use(async (ctx, next) => { // 表现层
await next()
})
.use(async (ctx, next) => { // 业务逻辑层
await next()
})
.use(async (ctx) => { // 数据访问层
ctx.body = await db.query('SELECT * FROM users')
})
Koa2中间件与分层架构
Koa2的中间件机制天然支持分层架构设计。每个中间件可以视为一个独立的层次,通过next()
函数控制执行流程。合理的中间件划分能够实现清晰的职责分离。
// 身份验证中间件(表现层)
app.use(async (ctx, next) => {
const token = ctx.headers['authorization']
if (!token) {
ctx.status = 401
return
}
ctx.state.user = verifyToken(token)
await next()
})
// 业务逻辑中间件
app.use(async (ctx, next) => {
if (ctx.path === '/users') {
ctx.state.users = await UserService.getAllUsers() // 调用业务逻辑层
}
await next()
})
模块划分策略
在Koa2项目中,模块划分通常基于业务功能。每个模块应该包含完整的层次结构,实现高内聚低耦合。
推荐的项目结构示例:
src/
├── modules/
│ ├── user/
│ │ ├── controllers/ # 表现层
│ │ ├── services/ # 业务逻辑层
│ │ ├── models/ # 数据访问层
│ │ └── routes.js # 路由定义
│ └── product/
│ ├── controllers/
│ ├── services/
│ ├── models/
│ └── routes.js
├── middlewares/ # 全局中间件
└── app.js # 应用入口
控制器层设计
控制器负责处理HTTP请求,应该保持精简,主要职责包括:
- 参数验证
- 调用服务层
- 返回响应
// user/controllers/userController.js
class UserController {
static async createUser(ctx) {
const { name, email } = ctx.request.body
// 参数验证
if (!name || !email) {
ctx.status = 400
ctx.body = { error: 'Name and email are required' }
return
}
// 调用服务层
try {
const user = await UserService.createUser({ name, email })
ctx.status = 201
ctx.body = user
} catch (error) {
ctx.status = 500
ctx.body = { error: error.message }
}
}
}
服务层设计
服务层包含核心业务逻辑,应该独立于HTTP上下文。良好的服务层设计应该:
- 不直接访问HTTP请求/响应对象
- 处理业务规则和流程
- 调用数据访问层
// user/services/userService.js
class UserService {
static async createUser(userData) {
// 业务逻辑验证
if (await this.emailExists(userData.email)) {
throw new Error('Email already exists')
}
// 调用数据访问层
return UserModel.create(userData)
}
static async emailExists(email) {
return UserModel.exists({ email })
}
}
数据访问层设计
数据访问层负责与数据库交互,应该封装所有数据操作细节。在Koa2中,通常使用ORM工具如Sequelize或Mongoose。
// user/models/userModel.js
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now }
})
// 静态方法
userSchema.statics = {
async exists(conditions) {
return this.countDocuments(conditions).then(count => count > 0)
}
}
module.exports = mongoose.model('User', userSchema)
跨层通信与依赖管理
分层架构中需要特别注意层间通信规则:
- 上层可以调用下层,但下层不能调用上层
- 避免循环依赖
- 使用依赖注入提高可测试性
// 使用依赖注入的示例
class UserController {
constructor(userService) {
this.userService = userService
}
async getUsers(ctx) {
ctx.body = await this.userService.getAllUsers()
}
}
// 在应用初始化时注入依赖
const userService = new UserService()
const userController = new UserController(userService)
router.get('/users', userController.getUsers.bind(userController))
异常处理策略
分层架构中,异常应该从底层向上层传播,最终由最上层的错误处理中间件统一处理。
// 自定义业务异常
class BusinessError extends Error {
constructor(message, statusCode = 400) {
super(message)
this.statusCode = statusCode
}
}
// 服务层抛出异常
class ProductService {
static async updateProduct(id, data) {
const product = await ProductModel.findById(id)
if (!product) {
throw new BusinessError('Product not found', 404)
}
// ...其他逻辑
}
}
// 全局错误处理中间件
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = err.statusCode || 500
ctx.body = {
error: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
}
})
测试策略与分层架构
分层架构便于单元测试和集成测试。每个层次可以独立测试,通过模拟(mock)依赖层。
// 测试服务层的示例
describe('UserService', () => {
let mockUserModel
beforeEach(() => {
mockUserModel = {
create: jest.fn(),
exists: jest.fn().mockResolvedValue(false)
}
})
it('should create user when email is unique', async () => {
const userData = { name: 'Test', email: 'test@example.com' }
mockUserModel.create.mockResolvedValue(userData)
const result = await UserService.createUser(userData, mockUserModel)
expect(mockUserModel.create).toHaveBeenCalledWith(userData)
expect(result).toEqual(userData)
})
})
性能优化考虑
分层架构可能引入性能开销,需要注意:
- 避免不必要的层间数据转换
- 批量操作尽量下沉到数据访问层
- 缓存常用数据
// 批量查询优化示例
class ProductService {
static async getProductsByIds(ids) {
// 一次性查询而不是循环查询
return ProductModel.find({ _id: { $in: ids } })
}
static async getProductsWithCache(ids) {
const cacheKey = `products:${ids.sort().join(',')}`
const cached = await cache.get(cacheKey)
if (cached) return cached
const products = await this.getProductsByIds(ids)
await cache.set(cacheKey, products, 3600) // 缓存1小时
return products
}
}
微服务架构下的分层调整
当Koa2应用作为微服务时,分层架构可能需要调整:
- 表现层增加RPC接口
- 业务逻辑层可能需要调用其他服务
- 数据访问层可能需要处理分布式事务
// 微服务中的服务层示例
class OrderService {
static async createOrder(orderData) {
// 调用用户服务验证用户
const userValid = await UserServiceClient.validateUser(orderData.userId)
if (!userValid) {
throw new BusinessError('Invalid user')
}
// 调用库存服务检查库存
const stockAvailable = await InventoryServiceClient.checkStock(
orderData.productId,
orderData.quantity
)
// 创建订单
return OrderModel.create(orderData)
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:服务层与业务逻辑封装