|
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 不存在则抛出异常,同时使用事务确保多表操作的原子性。
@libook 的确,后来我也想到了,但这样的话 createOrder 抛出的异常怎样比较好地转化到客户端异常呢?见 append 第三条
你 throw error 的时候是可以先 new 一个 Error 对象,然后给这个对象塞自己的自定义字段,然后 router 里 catch 到这个 error 之后拿你自定义字段看一眼,然后再根据当前 router 的情况决定如何返回 response 信息。这种做法会把 HTTP 协议层和业务逻辑层区分开,使得 createOrder 可以被多个 router 复用,每个 router 又能根据自己的情况来自定义返回。
|
|