Node服务端三层架构开发如何避免各层重复实现通用CRUD方法
Node三层架构CRUD重复代码优化方案
可以通过分层抽象通用基类/高阶函数的方式解决重复代码问题,三层分别做通用能力封装,业务层仅需要做差异化逻辑实现即可,具体方案如下:
1. 数据访问层(Model)通用封装
抽象通用BaseModel基类实现所有CRUD逻辑,业务Model继承基类传入配置即可使用通用能力,特殊查询可单独扩展:
// models/base/BaseModel.js class BaseModel { constructor(tableName, primaryKey = 'id') { this.db = require('../../config/db') this.tableName = tableName this.primaryKey = primaryKey } async get(filter = {}) { return this.db(this.tableName).where(filter) } async getById(id) { return this.db(this.tableName).where(this.primaryKey, id).first() } async create(data) { const [insertedId] = await this.db(this.tableName).insert(data) return this.getById(insertedId) } async updateById(id, data) { await this.db(this.tableName).where(this.primaryKey, id).update(data) return this.getById(id) } async deleteById(id) { return this.db(this.tableName).where(this.primaryKey, id).del() } } module.exports = BaseModel
业务Model使用示例:
// models/vacanciesData.js const BaseModel = require('./base/BaseModel') class VacanciesModel extends BaseModel { constructor() { super('vacancies', 'id') } // 特殊查询方法单独扩展,不影响通用逻辑 async getWithCompanyInfo(id) { return this.db(this.tableName) .join('companies', 'vacancies.company_id', 'companies.id') .where('vacancies.id', id) .first() } } module.exports = new VacanciesModel()
2. 服务层(Service)通用封装
抽象BaseService基类透传通用CRUD能力,业务Service继承后仅需要实现业务校验、字段处理等差异化逻辑:
// services/base/BaseService.js class BaseService { constructor(model) { this.model = model } get(filter) { return this.model.get(filter) } getById(id) { return this.model.getById(id) } create(data) { return this.model.create(data) } updateById(id, data) { return this.model.updateById(id, data) } deleteById(id) { return this.model.deleteById(id) } } module.exports = BaseService
业务Service使用示例:
// services/vacanciesService.js const BaseService = require('./base/BaseService') const vacanciesModel = require('../models/vacanciesData') class VacanciesService extends BaseService { constructor() { super(vacanciesModel) } // 差异化业务逻辑单独实现 async create(data, operatorId) { // 权限校验逻辑 if (!await checkOperatorAuth(operatorId, data.company_id)) { throw new Error('无操作权限') } data.code = `VAC_${Date.now()}` return super.create(data) } } module.exports = new VacanciesService()
3. 路由层通用封装
实现高阶函数自动生成通用CRUD路由,业务路由仅需要传入对应Service实例即可自动生成所有基础接口,特殊路由单独挂载:
// api/base/genericRouter.js const express = require('express') function generateGenericRouter(service, basePath = '/') { const router = express.Router() router.get(basePath, (req, res, next) => { service.get(req.query).then(result => res.json(result)).catch(next) }) router.get(`${basePath}:id`, (req, res, next) => { service.getById(req.params.id).then(result => res.json(result)).catch(next) }) router.post(basePath, (req, res, next) => { service.create(req.body).then(result => res.json(result)).catch(next) }) router.patch(`${basePath}:id`, (req, res, next) => { service.updateById(req.params.id, req.body).then(result => res.json(result)).catch(next) }) router.delete(`${basePath}:id`, (req, res, next) => { service.deleteById(req.params.id).then(result => res.json({ success: true })).catch(next) }) return router } module.exports = generateGenericRouter
业务路由使用示例:
// api/v1/vacancies.js const generateGenericRouter = require('../base/genericRouter') const vacanciesService = require('../../../services/vacanciesService') const router = generateGenericRouter(vacanciesService, '/') // 特殊路由单独扩展 router.get('/:id/company-info', async (req, res, next) => { try { const data = await vacanciesService.getWithCompanyInfo(req.params.id) res.json(data) } catch (err) { next(err) } }) module.exports = router
扩展说明
- 每层的通用方法均支持重写,遇到特殊业务场景可直接在子类覆盖对应方法,不会限制扩展性
- 参数校验、错误统一处理、权限校验等通用逻辑均可抽入对应基类实现全局统一管控
- 新增业务模块时仅需要继承三个基类传入对应配置,无需重复编写CRUD模板代码
内容的提问来源于stack exchange,提问作者Misha Kret
相关产品推荐
相关产品推荐

