web-dev-qa-db-ja.com

バージョン管理されたAPIの基礎となるコードベースをどのように管理しますか?

私はReST APIのバージョニング戦略について調べてきましたが、それらのどれも対処していないように見えるのは、基礎となるコードベースの管理方法です。

たとえば、単一のforenameフィールドではなく、個別のsurnameフィールドとnameフィールドを返すようにCustomerリソースを変更するなど、APIに多数の重大な変更を加えているとします。 (この例では、関連する概念を理解するのは簡単なので、URLバージョン管理ソリューションを使用しますが、質問はコンテンツネゴシエーションまたはカスタムHTTPヘッダーにも同様に適用できます)

http://api.mycompany.com/v1/customers/{id}にエンドポイントがあり、http://api.mycompany.com/v2/customers/{id}に別の互換性のないエンドポイントがあります。 v1 APIのバグ修正とセキュリティ更新をリリースしていますが、新機能の開発はすべてv2に焦点を当てています。 APIサーバーへの変更をどのように記述、テスト、展開しますか?少なくとも2つの解決策があります。

  • V1コードベースのソース管理ブランチ/タグを使用します。 v1とv2は独立して開発、展開され、必要に応じて両方のバージョンに同じバグ修正を適用するためにリビジョン管理マージが使用されます。

  • コードベース自体にAPIバージョンを認識させると、v1の顧客表現とv2の顧客表現の両方を含む単一のコードベースになります。展開の問題ではなく、ソリューションアーキテクチャの一部としてバージョン管理を扱います。おそらく名前空間とルーティングの組み合わせを使用して、要求が正しいバージョンで処理されるようにします。

ブランチモデルの明らかな利点は、古いAPIバージョンを削除するのが簡単なことです-適切なブランチ/タグのデプロイを停止するだけです-しかし、複数のバージョンを実行している場合、本当に複雑なブランチ構造とデプロイパイプラインになる可能性があります。 「統一されたコードベース」モデルはこの問題を回避しますが、廃止されたリソースとエンドポイントが不要になったときにコードベースから削除することをはるかに難しくします。単純な正しい答えはありそうにないので、これはおそらく主観的であることを知っていますが、複数のバージョンにわたって複雑なAPIを維持している組織がこの問題をどのように解決しているかを知りたいです。

83
Dylan Beattie

あなたが言及した戦略の両方を使用しました。これらの2つのうち、2つ目のアプローチは、それをサポートするユースケースでよりシンプルになります。つまり、バージョン管理のニーズが単純な場合は、より単純なソフトウェア設計を採用します。

  • 少ない変更数、低い複雑さの変更、または低い頻度の変更スケジュール
  • コードベースの残りの部分とほぼ直交する変更:パブリックAPIは、(過度の)(採用する用語の定義に対して)コードの分岐を必要とせずに、スタックの残りの部分と平和的に共存できます。

このモデルを使用して非推奨のバージョンを削除するのはそれほど難しくありませんでした。

  • 良好なテストカバレッジは、廃止されたAPIと関連するバッキングコードを削除することにより、回帰が(最小限の)確実に行われることを意味しました。
  • 適切な命名戦略(APIバージョンのパッケージ名、またはややugい、メソッド名のAPIバージョン)により、関連するコードを簡単に見つけることができました
  • 横断的関心事はより困難です。複数のAPIをサポートするコアバックエンドシステムの変更は、非常に慎重に検討する必要があります。ある時点で、バックエンドのバージョン管理のコスト(上記の「過剰」に関するコメントを参照)は、単一のコードベースの利点を上回ります。

最初のアプローチは、共存するバージョン間の競合を減らすという観点からは確かに単純ですが、別々のシステムを維持するオーバーヘッドは、バージョンの競合を減らす利点を上回る傾向がありました。とはいえ、新しいパブリックAPIスタックを立ち上げ、別のAPIブランチで反復を開始するのは非常に簡単でした。もちろん、世代の喪失はほぼ即座に始まり、ブランチはマージの混乱、マージの競合解決、およびその他のそのような楽しみに変わりました。

3番目のアプローチは、アーキテクチャレイヤーで行われます:Facadeパターンのバリアントを採用し、適切なFacadeインスタンスと通信する公開されたバージョン管理されたレイヤーにAPIを抽象化します。これは、独自のAPIセットを介してバックエンドと通信します。 Facade(以前のプロジェクトでアダプタを使用していました)が独自のパッケージになり、自己完結型でテスト可能になり、フロントエンドAPIをバックエンドおよび相互に独立して移行できます。

APIバージョンが同じ種類のリソースを公開する傾向がある場合に機能しますが、fullname/forename/surnameの例のように、異なる構造表現を使用します。 「バックエンドサービスが、パブリックAPI v1で公開された誤って計算された複利を返しました。お客様はすでにこの誤った動作にパッチを適用しています。」のように、異なるバックエンド計算に依存し始めると、少し難しくなります。バックエンドで計算し、v2まで適用します。したがって、今度は利息計算コードを分岐する必要があります。」幸いなことに、これらはめったにない傾向があります。実際には、RESTful APIのコンシューマーは、理論的にべき等のGETtedリソースに対する非破壊的な変更の中でも、バグごとの後方互換性よりも正確なリソース表現を好みます。

最終的な決定を聞きたいと思います。

38
Palpatim

分岐は私にとってはるかに良いようで、私の場合はこのアプローチを使用しました。

はい、すでに述べたとおり-バグ修正のバックポートには多少の労力が必要ですが、同時に1つのソースベース(ルーティングおよびその他すべてのもの)で複数のバージョンをサポートするには、少なからずとも同じ努力が必要です。内部にさまざまなロジックの分岐を伴う複雑で巨大なもの(バージョン管理のある時点で、巨大なcase()がコードを複製したバージョンモジュール、またはさらに悪いif(version == 2) then...)を指すようになります。また、回帰目的のために、テストを分岐させ続ける必要があることも忘れないでください。

バージョン管理ポリシーに関して:古いバージョンのサポートを廃止して、現在のバージョンから最大-2バージョンを維持します。

5
edmarisov