如何pipe理版本化API的底层代码库?

我一直在阅读关于ReST API的版本控制策略,而且他们似乎没有解决的是如何pipe理底层代码库。

比方说,我们正在对API进行一些重大更改 – 例如,更改我们的客户资源,以便返回单独的forenamesurname字段,而不是单个name字段。 (在这个例子中,我将使用URL版本解决scheme,因为它很容易理解所涉及的概念,但是这个问题同样适用于内容协商或自定义的HTTP头部)

我们现在在http://api.mycompany.com/v1/customers/{id}有一个端点,在http://api.mycompany.com/v1/customers/{id}另一个不兼容的端点。 我们仍在向v1 API发布错误修正和安全更新,但新function开发现在都集中在v2上。 我们如何编写,testing和部署对我们的API服务器的更改? 我可以看到至less有两个解决scheme:

  • 为v1代码库使用源代码控制分支/标记。 v1和v2是独立开发和部署的版本控制合并,在两个版本中都使用相同的修补程序 – 类似于在开发新版本时如何pipe理原生应用程序的代码库,同时还支持以前的版本。

  • 使代码库本身意识到API版本,所以你最终得到一个包含v1客户表示和v2客户表示的代码库。 将版本控制视为解决scheme架构的一部分,而不是部署问题 – 可能使用命名空间和路由的某种组合来确保请求由正确的版本处理。

分支模型的一个明显的优点是删除旧的API版本是很简单的 – 只要停止部署相应的分支/标签 – 但是如果你运行的是多个版本,那么最终可能会有一个非常复杂的分支结构和部署pipe道。 “统一的代码库”模式避免了这个问题,但是(我认为呢?)会使代码库中被废弃的资源和端点不再需要的时候更难。 我知道这可能是主观的,因为这不太可能是一个简单的正确答案,但我很好奇,要了解在多个版本中维护复杂API的组织是如何解决这个问题的。

我已经使用了你提到的两种策略。 在这两个方面,我赞成第二种方法,比较简单,在支持它的用例中。 也就是说,如果版本控制需求很简单,那么就用一个更简单的软件devise吧:

  • 变化less,复杂度低,变化频率低
  • 与其他代码库基本保持正交的变化:公共API可以与其余的栈和平共存,而不需要“过多”(无论您select采用哪种术语的定义)分支代码

我不觉得使用这个模型删除已弃用的版本是非常困难的:

  • 良好的testing覆盖率意味着剥离退出的API和相关的后台代码确保没有(很好,最小)的回归
  • 良好的命名策略(API版本的软件包名称,或方法名称中较为丑陋的API版本)可以很容易地find相关的代码
  • 交叉关切更难; 对核心后端系统的修改,以支持多个API必须非常仔细的权衡。 在某些时候,版本控制后端的成本(请参阅上面的“过度”评论)超过了单个代码库的好处。

从减less共存版本之间的冲突的angular度来看,第一种方法当然更简单,但维护单独系统的开销往往超过减less版本冲突的好处。 也就是说,站起来一个新的公共API堆栈并开始在一个单独的API分支上迭代是很简单的。 当然,世代stream失几乎是立即发生的,分支机构变成了一团糟,合并冲突的决议等这样的乐趣。

第三种方法是在架构层:采用Facade模式的变体,并将您的API抽象为面向公共的,版本化的层,与相应的Facade实例进行通信,然后通过自己的API集与后端进行通信。 你的Facade(我在之前的项目中使用了一个Adapter)变成了它自己的包,它是自包含的,可testing的,允许你独立于后端和彼此迁移前端API。

如果你的API版本倾向于公开相同types的资源,但是具有不同的结构表示,就像你的全名/名字/姓氏例子那样。 如果他们开始依赖于不同的后端计算,这会变得稍微困难​​一些,比如“我的后端服务已经返回错误地计算出来的公开API v1中的复合利息了,我们的客户已经修补了这个不正确的行为,所以我不能更新在后端进行计算,直到v2才有效,所以我们现在需要分配我们的兴趣计算代码。 幸运的是,这些通常不经常发生:实际上,RESTful API的消费者更喜欢准确的资源表示,而不是针对错误的向后兼容性,甚至是对理论上幂等的GET资源的非重大改变。

我会有兴趣听到你最终的决定。

对我来说,第二种方法更好。 我已经将其用于SOAP Web服务,并计划将其用于REST。

在编写代码时,代码库应该是版本识别的,但兼容层可以用作单独的图层。 在你的例子中,代码库可以产生具有名字和姓氏的资源表示(JSON或者XML),但是兼容性层将它改变为仅具有名字。

代码库应该只实现最新版本,可以说v3。 兼容层应该在最新版本v3和支持的版本(例如v1和v2)之间转换请求和响应。 兼容层可以为每个支持的版本都有独立的适配器,可以作为链接进行连接。

例如:

客户v1请求:v1适应v2 —> v2适应v3 —-> codebase

客户端v2请求:v1适应v2(跳过)—> v2适应v3 —-> codebase

对于响应,适配器只是在相反的方向上起作用。 如果您使用的是Java EE,那么您可以将servlet筛选器链作为适配器链来举例说明。

删除一个版本很容易,删除相应的适配器和testing代码。

分支似乎对我来说好多了,我用这种方法来处理。

是的,正如你已经提到的 – 反向错误修复需要一些努力,但同时支持一个源代码下的多个版本(包括路由和所有其他的东西)将需要你,如果不less,但至less同样的努力,使系统更多复杂和怪异的逻辑里面的不同分支(在某些版本你点定义会来到巨大的case()指向版本模块的代码重复,或更糟, if(version == 2) then... )。 也不要忘记,为了回归的目的,你仍然需要保持testing分支。

关于版本策略:我会保留最新版本,不再支持旧版本 – 这会为用户提供一些动力。