You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 22:45:01