详解Nodejs Express.js项目架构

发布时间:2025-09-21 点击:27
视频教程推荐:nodejs 教程
引言
在 node.js 领域中,express.js 是一个为人所熟知的 rest apis 开发框架。虽然它非常的出色,但是该如何组织的项目代码,却没人告诉你。
通常这没什么,不过对于开发者而言这又是我们必须面对的问题。
一个好的项目结构,不仅能消除重复代码,提升系统稳定性,改善系统的设计,还能在将来更容易的扩展。
多年以来,我一直在处理重构和迁移项目结构糟糕、设计不合理的 node.js 项目。而这篇文章正是对我此前积累经验的总结。
目录结构
下面是我所推荐的项目代码组织方式。
它来自于我参与的项目的实践,每个目录模块的功能与作用如下:
src │ app.js # app 统一入口 └───api # express route controllers for all the endpoints of the app └───config # 环境变量和配置信息 └───jobs # 队列任务(agenda.js) └───loaders # 将启动过程模块化 └───models # 数据库模型 └───services # 存放所有商业逻辑 └───subscribers # 异步事件处理器 └───types # typescript 的类型声明文件 (d.ts)而且,这不仅仅只是代码的组织方式…
3 层结构
这个想法源自 关注点分离原则,把业务逻辑从 node.js api 路由中分离出去。
因为将来的某天,你可能会在 cli 工具或是其他地方处理你的业务。当然,也有可能不会,但在项目中使用api调用的方式来处理自身的业务终究不是一个好主意…
不要在控制器中直接处理业务逻辑!!
在你的应用中,你可能经为了图便利而直接的在控制器处理业务。不幸的是,这么做的话很快你将面对相面条一样复杂的控制器代码,“恶果”也会随之而来,比如在处理单元测试的时候不得不使用复杂的 request 或 response 模拟。
同时,在决定何时向客户端返回响应,或希望在发送响应之后再进行一些处理的时候,将会变得很复杂。
请不要像下面例子这样做.
route.post('/', async (req, res, next) => { // 这里推荐使用中间件或joi 验证器 const userdto = req.body; const isuservalid = validators.user(userdto) if(!isuservalid) { return res.status(400).end(); } // 一堆义务逻辑代码 const userrecord = await usermodel.create(userdto); delete userrecord.password; delete userrecord.salt; const companyrecord = await companymodel.create(userrecord); const companydashboard = await companydashboard.create(userrecord, companyrecord); ...whatever... // 这里是“优化”,但却搞乱了所有的事情 // 向客户端发送响应... res.json({ user: userrecord, company: companyrecord }); // 但这里的代码仍会执行 :( const salaryrecord = await salarymodel.create(userrecord, companyrecord); eventtracker.track('user_signup',userrecord,companyrecord,salaryrecord); intercom.createuser(userrecord); gaanalytics.event('user_signup',userrecord); await emailservice.startsignupsequence(userrecord) });使用服务层(service)来处理业务
在单独的服务层处理业务逻辑是推荐的做法。
这一层是遵循适用于 node.js 的 solid 原则的“类”的集合
在这一层中,不应该有任何形式的数据查询操作。正确的做法是使用数据访问层.
从 express.js 路由中清理业务代码。
服务层不应包含 request 和 response。
服务层不应返回任何与传输层关联的数据,如状态码和响应头。
示例
route.post('/', validators.usersignup, // 中间件处理验证 async (req, res, next) => { // 路由的实际责任 const userdto = req.body; // 调用服务层 // 这里演示如何访问服务层 const { user, company } = await userservice.signup(userdto); // 返回响应 return res.json({ user, company }); });下面是服务层示例代码。
import usermodel from '../models/user'; import companymodel from '../models/company'; export default class userservice { async signup(user) { const userrecord = await usermodel.create(user); const companyrecord = await companymodel.create(userrecord); // 依赖用户的数据记录 const salaryrecord = await salarymodel.create(userrecord, companyrecord); // 依赖用户与公司数据 ...whatever await emailservice.startsignupsequence(userrecord) ...do more stuff return { user: userrecord, company: companyrecord }; } }在 github 查看示例代码
https://github.com/santiq/bulletproof-nodejs
使用发布/订阅模式
严格来讲发布/订阅模型并不属于 3 层结构的范畴,但却很实用。
这里有一个简单的 node.js api 用来创建用户,于此同时你可能还需要调用外部服务、分析数据、或发送一连串的邮件。很快,这个简单的原本用于创建用户的函数,由于充斥各种功能,代码已经超过了 1000 行。
现在是时候把这些功能都拆分为独立功能了,这样才能让你的代码继续保持可维护性。
import usermodel from '../models/user'; import companymodel from '../models/company'; import salarymodel from '../models/salary'; export default class userservice() { async signup(user) { const userrecord = await usermodel.create(user); const companyrecord = await companymodel.create(user); const salaryrecord = await salarymodel.create(user, salary); eventtracker.track( 'user_signup', userrecord, companyrecord, salaryrecord ); intercom.createuser( userrecord ); gaanalytics.event( 'u

在阿里云服务器在腾讯云备案可以
网站建设良好的网站导航把握以下几点原则
海外web云服务器折扣
免费的手机建站工具 包你一学就会
过期一天了里面东西还在吗
服务器局域网访问-云服务器问题
已续费请开机-虚拟主机/数据库问题
广东惠州富华桥机械设备有限公司请问这是怎么回事