API 工具链研发的理论基础 - 流派

定义一个 API,都要定义哪些信息?

在乌托邦世界的理想国里,有一家企业,他提供的服务被世界上许许多多的客户使用着。人们都希望,能够使用自己趁手的工具/编程语言,来享受该服务带来的便利。

小欧是一个初入职场的年轻工程师,他怀着对前路的憧憬和对未来的迷茫加入了它,希望能帮助这家企业打造出好用的工具产品,满足用户的期待。

企业对外公开了大量的 API,用户通过自己趁手的编程语言来编写程序,编排 API,创建自己的需要的产品实例。

我们的故事,从小欧面临的第一个问题开始说起:API 是如何定义的。

源流

为了弄清楚这个问题,小欧研究了乌托邦世界中几乎所有的 API 设计工具,总结出了它们的一些共同的构成。

首先是结构类信息,包括 API 的:

  • 组织方式,如何划分命名空间,例如按照不同的产品将 API 的定义分开,甚至可以采用多级的命名空间划分,比如产品大类 - 产品小类等等
  • 抽象粒度,比如我们如果以一次 API 调用作为一个整体来看待的话,那么这些 API 应该如何分布在命名空间中?是以资源,还是以服务的形式来组织?和网络协议相关的声明(比如 HTTP 动词),应该在哪个层级中被定义?
  • 基本构成,包括 API 的请求、响应以及错误信息的定义

其次是特性类信息,一般指用于特种用途,主要由周边系统来消费的信息。例如,SDK 的包名、国际化(i18n)的文本、API 文档的告示等等。

通常情况下结构类信息统一在 API 管理平台中来维护。而特性类信息则通常用于周边的系统,可以在系统中进行标注,也可以从周边的系统中抽取过来。结构类信息与特性类信息呈正交关系,一类信息的缺失并不会对另一类信息产生影响。

最后是类型系统,即对于 API 的定义,如何标注可以使其描述请求的和响应的类型信息。类型系统的完备性直接决定了是否可以完成后续的制品(例如 SDK、工具产品)生成的工作,同时也对 API 的编写提供了一定的约束能力。

一个较为完备的类型系统应提供以下几种类型:

  • 基本类型(Base/Primitive Type)
  • 复合类型
    • 数组类型(Array Type)
    • 对象类型(Object/Record Type)
  • 联合类型(Union/Sum Type)(可选)

编者按:关于类型系统的知识,这里就不过多介绍了,感兴趣的读者可以看附录中的参考文献。

作为一个经验丰富的工程师,小欧觉得,采用更为主流的技术,从软件工程的角度来说更加稳妥。

小欧决定稳一手,先找个开源方案用起来。

抉择

在调研的过程中,小欧发现,企业内部似乎已经存在了多种 API 主流方案的流派,选择之旅,似乎在一开始就夭折了。

因为面前有两头拦路虎:

  • 描述能力:企业中有大量风格迥异的存量 API,无法放弃,单一开源工具的描述能力不足以覆盖这些企业定制化的情形,包括但不限于 RPC 风格的 HTTP API、自定义的错误码格式,以及对于类型系统的扩展需求(例如枚举值需要定义三要素、命名、值和描述信息)。
  • 约束能力:开源工具需要满足绝大多数人的需求,所以在约束能力方面往往比较宽松,例如许多工具支持将字段类型声明为动态类型或联合类型,而一些企业可能需要更严格的约束。

那么如何既拥抱主流的方案,又能够解决企业遇到的难题呢?经过科学的软件工程训练的小欧,自然想到了 SOLID 原则中的里式替换原则(Liskov Substitution principle)。

我们完全可以派生出一个完全符合企业现状的 API 声明格式,并且在其它的方案上实现一个转换逻辑,只要保证新的格式在我们关注领域的描述能力是主流方案的超集,那么在这个领域我们就可以新旧两种方案并存。我们当我们需要新方案的描述能力时,就可以切换到新方案去编写。

我们的小欧一拍脑袋,“嗨,这不难嘛,我也能写!”。

小欧花了 5 分钟时间,写下了这样的 API:

- namespace: ooo
  services:
  - name: myservice
    apis:
    - name: myapi
      request:
      - name: foo
        type: string
        format: uuid
      response:
      - name: bar
        type: array
        items: { type: string }
      errors:
      - code: 42
        message: "cause by chaos monkey"
      traits:
        i18n: myapi
    traits:
      sdk: myservice

编者按:文中示例仅供参考,实际生产中的用法因为需要兼顾一些工程细节,会显得更为复杂些

优雅清爽、足够简单,API 定义的问题真的就这样解决了吗?

此时的小欧所不知道的是,更大的难题正在向他悄悄袭来。

参考阅读

项目实践:

经典书籍:

课程讲义:

  • 《编程语言的设计原理》,北大熊英飞老师开设的一门课程,其中涉及了类型系统的理论基础
Some rights reserved
Except where otherwise noted, content on this page is licensed under a Creative Commons Attribution-ShareAlike 4.0 International license