前段时间组内搞代码检视,常常能看到一些 “挂着 RESTful 羊头,卖的却是 GraphQL 狗肉”的 API 设计。
举个例子,如果后盾有两种资源用户 User 和 群组 Group ,依照RESTful的标准,他们设计以下API端点:

# 获取用户列表GET /users# 获取指定用户GET /user/{id}# 创立用户POST /users# 批改用户PUT /user/{id}# 删除用户DELETE /user/{id}# 获取群组列表GET /groups# 获取指定群组GET /group/{id}# 创立群组POST /groups# 批改群组PUT /group/{id}# 遣散群组DELETE /group/{id}

咋一看没啥问题,可是到了前面,要实现 “用户退出群组” 和 “用户退出群组” 两个个性时,他们给出的API设计:

# 用户退出群组PATCH /group/{id}{  "addMembers": ["userID"]}# 用户退出群组PATCH /group/{id}{  "deleteMembers": ["userID"]}

我当场就蚌埠住了,这哪里是 RESTful 格调,这不就是他们老大深痛恶绝的 GraphQL ?
眼看我就要当场发飙,有个新手连忙进去圆场,他给出以下API设计:

# 用户退出群组PATCH /user/{id}/group/{groupID}# 用户退出群组DELETE /user/{id}/group/{groupID}

这个设计看起来是解决方才的问题,但实际上只是自欺欺人。
我接着诘问: “管理员邀请用户退出群组” 和 “管理员将用户踢出群组” 要怎么设计呢?
那个新手依瓢画葫芦给出以下设计:

# 管理员邀请用户退出群组PATCH /group/{id}/user/{userID}# 管理员将用户踢出群组DELETE /group/{id}/user/{userID}

眼看他们还不觉悟,我就接着诘问:“用户退出群组须要管理员批准” 和 “管理员邀请用户退出群组须要用户批准” 又怎么实现呢?
这下子新手也没辙,只能说这块的代码须要从新设计。

我让他们把原来的设计文档从新拿进去检视,而后发现很多中央都不符合规范,连根本的实体关系图ER都没有!
我忍不住吐槽:怪不得当年我一个人拿着不到一半的工资,连前后端一起搞,效率却比这帮985/211的高材生/海归还要高,而他们连一个最根本的CURD都要一直地返工!

原来招人的HR齐全不思考业余是否对口,只看毕业院校和学历,后果这帮非科班出身的人连ER图都不会,更别说更简单一点的类图、流动图以及泳道图,要晓得对科班出身的人来说画UML是基本功!

其实他们的设计外面曾经有了 User 和 Group 两张实体表,再减少一张 Relation 关系表:

struct Relation {id stringuser_id stringgroup_id stringstatus string # 依据cookie的session获取以后用户id# 再判断以后用户是不是以后group的管理员# 如果是则管理员邀请用户,把status设置为"wait_for_admin_approve"# 否则就是用户申请加入群组,把status设置为"wait_for_user_accept"}

而后这样设计API:

# 用户退出群组POST /relations{  user: "abc",  group: "ikun"}# 用户退出群组DELETE /relations/{id}# 管理员邀请用户退出群组POST /relations{  user: "abc",  group: "ikun"}# 管理员将用户踢出群组DELETE /relations/{id}

回头总结,不难发现,大多数人在实际RESTful标准时,最容易犯的谬误就是解决嵌套资源的时候,容易设计出 /parent/xxx/sub/xxx 这样的API。

这种设计咋一看起来的确非常容易了解,但其实前期十分难以保护,尤其是遇到 n:m 多对多的关系时,这种嵌套的API设计就是一场劫难。

即使是 1:n 一对多的状况下,嵌套的API设计看起来没有问题,但前期当子资源 n 越来越大的时候,独自减少/删除某个子资源须要把所有子资源都获取一遍,就非常容易造成性能瓶颈,并且没方法通过分库分表的形式进行横向扩容。

只管咱们能够换成GraphQL的格调,通过 addSubdelSub 的形式独自新增或删除某个子资源,来防止每次都须要获取所有的子资源。

但咱们也必须要思考并发更新资源频繁引发抵触的危险;尤其越来越多的Java开发转向基于Go的云原生开发,为了不便保护不再额定引入SQL数据库,间接应用etcd,但etcd自身也有mvcc机制来保证数据的一致性,这样还是会导致并发更新资源的抵触!

通过这次事件,我也彻底明确了,真正把握RESTful标准,离不开对关系模型的深厚功底;只管咱们从SQL数据库切换到etcd这类NoSQL数据库,按情理API设计规范应该相应地换成GraphQL与对象模型严密相连的标准。

但思考团队程度参差不齐,后盾API接口设计规范还是要保持RESTful为主,先把基于关系数据库的基本功练好!

前端团队是能够基于GraphQL标准实现Backend for Frontend,搞一个接口聚合模块,作为性能优化伎俩,缩小首页白屏工夫。