Nodejs后端业务逻辑代码如何分离
Nodejs后端业务逻辑代码如何分离
node+koa ,有这样一个接口:
router.post(
'/order',
{...},
async ctx => {
const { body } = ctx.request
// 一些需要查询数据库的参数验证逻辑,比如:
const item = await Item.findOne({ where: { id: body.id } })
if (!item) {
throw Error('item not existing')
}
// 后续逻辑
const result = await createOrder(body)
ctx.body = result.id
}
)
createOrder
这个函数因为其他接口 /逻辑也会用到所以单独抽出来了,于是产生个问题:
函数内部也有用到查询Item
的地方,那这样的话,/order
这一个接口得查询Item
两次。例子是查一个,查列表的情况岂不是更浪费。
抽象出来大概是:如何避免 通用业务逻辑 与 调用它的 route 之间重复相同数据库查询操作?
求赐教!谢谢!
createOrder 增加一个可空参数 item 呗,存在就不查数据库,不存在再查。
createOrder 传参 Item
如果 Item 是必须查询的,类似 List 于列表页,Post 于文章页,那也可以查询出来放到一个全局变量 queried object ,后续逻辑要获取当前查询对象时读取这个变量就行
查还是要查的,你可以写成中间件的形式,把查数据库的验证逻辑写在中间件就不用重复这么多次了,应该是可行的
一种思路是如楼上所说,item 传参给 createOrder 。
另外一种思路:item 必须存在是否也是 createOrder 成功的必要条件?如果是的话,这个判断可以放到 createOrder 内部去做,这样也能避免其他调用 createOrder 的地方也提前去查询 item 是否存在。
orm 托管的话,如果 findone 的 id 相同可以选择直接引用内存中的 item 对象,而不去数据库中查询
Item 里面封装校验逻辑,能被创建出来就是合法的,调用时只管用,这样就能将通用业务逻辑和其他业务逻辑分开
其实重复查两次的的问题还好,顶多是浪费一部分数据库性能而已,不会导致 bug ;目前的实现是有可能产生 bug 的,即在高并发场景下 createOrder 执行的时候不一定确保 item 一定存在,假设 item 有可能在“const item = await Item.findOne({ where: { id: body.id } })”执行后、“createOrder”执行前被其他进程的业务逻辑删除,那么此时 createOrder 就会在 item 不存在的情况下执行;若 createOrder 内部做了 item 的存在性验证,此时会抛出异常;若未做 item 的存在性验证,则会产生脏数据。同理 createOrder 内部的多项数据库操作如果不能确保事务性(原子性)的话,也会有同样的问题。
针对题主的例子来说,item 的存在性验证仅为 createOrder 的顺利执行而服务,那么可以把这个过程并入 createOrder 内部,在 createOrder 内部查询 item 不存在则抛出异常,同时使用事务确保多表操作的原子性。
的确,后来我也想到了,但这样的话 createOrder 抛出的异常怎样比较好地转化到客户端异常呢?见 append 第三条
你 throw error 的时候是可以先 new 一个 Error 对象,然后给这个对象塞自己的自定义字段,然后 router 里 catch 到这个 error 之后拿你自定义字段看一眼,然后再根据当前 router 的情况决定如何返回 response 信息。这种做法会把 HTTP 协议层和业务逻辑层区分开,使得 createOrder 可以被多个 router 复用,每个 router 又能根据自己的情况来自定义返回。
在Node.js项目中,将后端业务逻辑代码分离是提高代码可维护性和可读性的关键步骤。通常,我们会采用模块化编程的方式来实现这一点。以下是一个简单的示例,展示了如何将业务逻辑从控制器中分离出来。
首先,我们创建一个 controllers
文件夹,用于存放控制器文件,如 userController.js
。然后,我们创建一个 services
文件夹,用于存放业务逻辑文件,如 userService.js
。
userService.js
示例:
// userService.js
module.exports = {
getUserById: async (id) => {
// 这里模拟数据库查询
return { id, name: 'John Doe' };
},
// 其他业务逻辑函数...
};
userController.js
示例:
// userController.js
const express = require('express');
const router = express.Router();
const userService = require('../services/userService');
router.get('/user/:id', async (req, res) => {
try {
const user = await userService.getUserById(req.params.id);
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;
在 app.js
中,我们只需要引入控制器并挂载到路由上:
const express = require('express');
const app = express();
const userController = require('./controllers/userController');
app.use('/api', userController);
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
通过这种方式,我们可以轻松地将业务逻辑与控制器分离,使代码更加清晰和易于管理。