RESTful 的收益是什么?


RESTful 是那种咋一听很有道理,仔细思考一下又觉得然并卵的东西。在乘法云的设计中,IO Adapter 实质上就是把各种外部的 API,统一适配成 RESTful 的风格。回答了 RESTful 的收益是什么,也就回答了为什么需要搞一层 IO Adapter。

RESTful 的本质

RESTful 分为两块:
  • HATEOAS (Hypermedia as the Engine of Application State)
  • 对 operation 进行了分类


本文只讨论对 operation 分类这部分。所谓 operation-oritented 的 API,就是大家熟知的 RPC。而 resource-oriented 的 API,就是所谓的 RESTful。把 API 设计成 RESTful 的就是把 operation 分成以下4类:
  • GET - read
  • POST - create
  • PUT - update
  • DELETE - delete


约定就是 POST 之后的创建的 resource 可以通过 id GET 回来。PUT 更新了的字段,再次 GET 可以看到更新。DELETE 删掉的 resource 无法自此通过 id GET 回来。例如一篇博客文章,Article。

因为很多 operation 并不能满足上面的约定,所以就感觉无法变成 RESTful 的。比如,假设我们有一个概念叫外卖订单 Order。那么这个订单就需要支持已下单,商家接单,骑手到店,骑手开始配送,已收货等多种操作。我们不可能让 Order 支持 PUT 操作,直接允许对 status 的修改。所以对 Order 来说,只有三个操作:
  • GET - loadOrder, queryOrder
  • POST - createOrder
  • DELETE - deleteOrder


对订单的更新不能用 PUT,只能用创建命令的形式(可以想象为给 order 办公室提交了一个修改申请的公文),间接进行修改。以商家接单为例,命令叫 MerchantConfirmation,对应的 operation 应该分为:
  • POST - createMerchantConfirmation
  • GET - getMerchantConfirmation
  • DELETE - deleteMerchantConfirmation


通过 RESTful 的 sub-resource 的概念,我们可以表达两个 resource 之间这种紧密的关系:
POST /order/xxx/merchantConfirmation

也就是 RESTful 把多个关联的 operation 进行了分类,哪些是 GET 哪些是 POST。然后又把 resource 之间进行了远近关系的分类,紧密的 resource 变成 sub-resource。

进一步分类

沿着分类的思路。我们可以看到博客文章 Article 和外卖订单 Order 以及 MerchantConfirmation其实很不一样。我们可以把 resource 分为以下几类:
  • Passive Record:完整支持 CRUD 四种操作
  • Party/Place/Thing:生命周期长,一般没有状态机,但是部分字段不支持 PUT 操作
  • Moment Interval(Business Transaction):生命周期短,一般有状态机,所有字段不支持 PUT 操作
  • Immutable Command/Event:POST 的时候是 Command,GET 的时候就变成 Event 了。


Passive Record 就是类似博客文章 Article 这样的东西。其特点是业务约束或者合同几乎没有,或者说交易发生在线下。所以线上的数据只是一份被动的记录。比如说今天的天气,这个记录没有什么可商量的余地,测量得是什么就是什么。

Party/Place/Thing 和 Moment Interval 就是带有业务约束的数据。因为业务约束的存在,所以无法保证 PUT 之后的改动,能 GET 回来。PUT 其实就是最简单的业务逻辑,写啥是啥。所以有业务约束保护的字段或者整个 resource 是无法支持 PUT 的。例如司机档案,对于一些无关紧要的字段,例如司机的兴趣爱好,随便 CRUD 都可以。但是司机的年龄,司机的驾照,这些信息和这个司机是否能在平台上接单直接相关,是绝对不允许随意 PUT 的。

Moment Interval 一般指订单这样的东西,相比司机,客户,账户这些概念,生命周期要短很多。而且一般都有强的状态机的约束。对于 Moment Interval 的修改,都需要用 POST 创建一个 command 的方法来间接修改,不能直接 PUT。我们可以把这些 command 建模成 sub-resource。

如果一个 resource 从创建,到创建完成就不再变化了。那么它就是一个 Immutable Command / Event。对于同步操作,就是 POST 的时候,直接完成处理固化成一个 Event 了。如果是异步操作,那么 POST 的时候还是 command,有一个 id。然后再次 GET 的时。候,就可以知道 command 是不是已经变成 event 了。例如食客提交一个取消订单的申请(OrderCancel),这个申请需要10分钟内得到商家的确认是同意还是拒绝,当商家给出答复之后这个命令就变成了一个已经发生的事件了。可以认为 OrderCancel 这个 resource 是由 OrderCancelCommand 和 OrderCancelEvent 两部分构成的。当创建的时候,只有 command 部分。在处理完成了之后,OrderCancel 才是一个完整的 event。

command 又可以分为两种,作为 sub-resource 的 command,和自由的 command。如果一个 command 的处理过程中会修改很多个 resource,那么就不适合分类为某个 resource 的 sub-resource 了。

event 暴露出 GET 的接口,并支持 atom 的协议,就可以替代 event bus 或者 message queue 用轮询来实现系统之间的通知的语义了。因为 Event 自身具有 immutable 的属性,所以这些 GET 接口,带上时间段就可以是永久缓存的。

RESTful 的收益

RESTful 的技术层面的收益,jim webber 等人都已经讲得非常清楚了。无非就是因为分类,使得中间的 middle-ware 可以根据类型做一些预定义的动作。另外一个额外的好处是因为 POST 的 command,需要 GET 回来。一些同步的操作,在 RESTful 之后我们会更倾向于建模成异步的,瞬时的操作会持久下来。这样会导致很多错误场景更好处理,系统也会在错误的发生的时候更健壮,考虑的边界条件会更全面。

RESTful 在编程心理学层面的收益才是更主要的。一个最显然易见的收益是通过统一 API 成为整齐一致的风格,减少了学习成本。然后通过分类,它使得 API 的用户能够很好的复用先前的知识。比如程序员都知道 POST 了之后可以 GET,所以这两个 operation 的关系通过分类就不言自明了。如果我们能把 resource 进一步分为 passive record,moment interval 等类型,那么 API 的用户又可以进一步复用已知的知识,快速把新概念套到旧的刻板印象里。噢,这个东西是个有状态机的 moment interval,那个东西是一个 immutable event。

另外一方面通过动词名化,我们引入了名词概念。再通过 sub-resource 之类的手段,让这些名词之间映射到几何空间,具有距离上的远近。从认知上来说,这种具有某种空间意义的名词,更容易留下印象,在头脑中构建出“meaning”。

所以就像所有高级编程语言写的程序,都有等价的汇编写法一样。不能说 RESTful 最终“干的事情”和 RPC 没有差别,就否定 RESTful 的价值。毕竟代码是写给人来看的。RESTful 通过分类,降低了代码的理解成本,对提高开发的效率是有收益的。

原文链接:https://zhuanlan.zhihu.com/p/80724757,作者:陶文

0 个评论

要回复文章请先登录注册