乐趣区

关于restful:2023再谈RESTful-和-GraphQL

前段时间组内搞代码检视,常常能看到一些“挂着 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 string
user_id string
group_id string
status 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,搞一个接口聚合模块,作为性能优化伎俩,缩小首页白屏工夫。

退出移动版