请教后端业务逻辑代码如何分离

2021-11-20 23:41:40 +08:00
 px920906

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 之间重复相同数据库查询操作?

求赐教!谢谢!

4793 次点击
所在节点    Node.js
9 条回复
lscho
2021-11-20 23:46:53 +08:00
createOrder 增加一个可空参数 item 呗,存在就不查数据库,不存在再查。
eason1874
2021-11-21 00:04:17 +08:00
createOrder 传参 Item

如果 Item 是必须查询的,类似 List 于列表页,Post 于文章页,那也可以查询出来放到一个全局变量 queried object ,后续逻辑要获取当前查询对象时读取这个变量就行
towave
2021-11-21 00:20:43 +08:00
查还是要查的,你可以写成中间件的形式,把查数据库的验证逻辑写在中间件就不用重复这么多次了,应该是可行的
ElmerZhang
2021-11-21 00:27:19 +08:00
一种思路是如楼上所说,item 传参给 createOrder 。
另外一种思路:item 必须存在是否也是 createOrder 成功的必要条件?如果是的话,这个判断可以放到 createOrder 内部去做,这样也能避免其他调用 createOrder 的地方也提前去查询 item 是否存在。
zjsxwc
2021-11-21 08:33:57 +08:00
orm 托管的话,如果 findone 的 id 相同可以选择直接引用内存中的 item 对象,而不去数据库中查询
OutOfMemery
2021-11-21 12:32:42 +08:00
Item 里面封装校验逻辑,能被创建出来就是合法的,调用时只管用,这样就能将通用业务逻辑和其他业务逻辑分开
libook
2021-11-22 10:53:26 +08:00
其实重复查两次的的问题还好,顶多是浪费一部分数据库性能而已,不会导致 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 不存在则抛出异常,同时使用事务确保多表操作的原子性。
px920906
2021-11-23 21:49:58 +08:00
@libook 的确,后来我也想到了,但这样的话 createOrder 抛出的异常怎样比较好地转化到客户端异常呢?见 append 第三条
libook
2021-11-24 10:27:58 +08:00
你 throw error 的时候是可以先 new 一个 Error 对象,然后给这个对象塞自己的自定义字段,然后 router 里 catch 到这个 error 之后拿你自定义字段看一眼,然后再根据当前 router 的情况决定如何返回 response 信息。这种做法会把 HTTP 协议层和业务逻辑层区分开,使得 createOrder 可以被多个 router 复用,每个 router 又能根据自己的情况来自定义返回。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/816854

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX