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


104

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

我々は、APIへの変更を壊すの束を作っているとしましょう-例えば、それは別の返すように私たちのお客様のリソースを変更forenameし、surname代わりに、単一のフィールドnameのフィールド。(この例では、関連する概念を理解するのは簡単なので、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を維持している組織がこの問題をどのように解決しているかを知りたいと思います。


41
この質問をお寄せいただきありがとうございます!もっと多くの人がこの質問に答えていないなんて信じられません!私は、バージョンがシステムにどのように入るのかについて皆が意見を持っていることにうんざりしていますが、適切なコードにバージョンをディスパッチするという本当に難しい問題に取り組む人はいません。今では、この一見共通の問題に対して、少なくとも一連の「パターン」または「解決策」が受け入れられるはずです。「APIバージョン管理」に関するSOに関する非常に多くの質問があります。バージョンを受け入れる方法を決定することは、FRIKKIN SIMPLE(比較的)です!いったんコードベースに入るとそれを処理するのは難しいです!
arijeet

回答:


45

私はあなたが言及した戦略の両方を使用しました。これら2つのうち、私は2番目のアプローチを支持します。つまり、バージョン管理のニーズが単純な場合は、より単純なソフトウェア設計を使用します。

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

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

  • 優れたテストカバレッジとは、廃止されたAPIと関連するバッキングコードを完全に削除することで、退行がないことを保証することです
  • 優れたネーミング戦略(APIバージョンのパッケージ名、またはメソッド名のやや醜いAPIバージョン)により、関連するコードを簡単に見つけることができました
  • 分野横断的な懸念はより困難です。複数のAPIをサポートするためのコアバックエンドシステムへの変更は、慎重に検討する必要があります。ある時点で、バックエンドのバージョン管理のコスト(上記の「過剰」に関するコメントを参照)は、単一のコードベースの利点を上回ります。

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

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

これは、APIのバージョンが同じ種類のリソースを公開する傾向があるが、フルネーム/フォアネーム/姓の例のように構造表現が異なる場合に機能します。たとえば、「バックエンドサービスが、パブリックAPI v1で公開されている誤って計算された複利を返しました。お客様がこの誤った動作にパッチをすでに適用しているため、更新できません。バックエンドでの計算とそれをv2まで適用します。したがって、今度は金利計算コードをフォークする必要があります。」幸いなことに、それらはまれである傾向があります。実際には、RESTful APIのコンシューマーは、理論的にべき等なGETtedリソースの非破壊的な変更の中でも、バグとバグの下位互換性よりも正確なリソース表現を優先します。

私はあなたの最終的な決定を聞いて興味があります。


5
奇妙なことに、ソースコードで、変更されなかったv0とv1の間でモデルを複製しますか?または、v1で一部のv0モデルを使用していますか?私にとって、いくつかのフィールドでv1モデルがv0モデルを使用しているのを見ると混乱するでしょう。しかし、その一方で、コードの肥大化を減らすでしょう。複数のバージョンを処理するために、変更されていないモデルの重複コードを受け入れ、それと共存する必要がありますか?
EdgeCaseBerg 2017

1
私の記憶は、API自体とは関係なくソースコードのバージョン管理されたモデルであるため、たとえば、API v1はモデルV1を使用し、API v2もモデルV1を使用する可能性があるということです。基本的に、パブリックAPIの内部依存関係グラフには、公開されたAPIコードと、サーバーやモデルコードなどのバックエンド「フルフィルメント」コードの両方が含まれていました。複数のバージョンの場合、私が今まで使用した唯一の戦略はスタック全体の複製です-ハイブリッドアプローチ(モジュールAが複製され、モジュールBがバージョン管理される...)は非常に混乱します。もちろんYMMV。:)
Palpatim 2017

2
3番目のアプローチの提案に従うかどうかはわかりません。そのような構造のコードの公開例はありますか?
Ehtesh Choudhury

13

私にとっては、2番目のアプローチの方が優れています。SOAP Webサービスに使用しており、RESTにも使用する予定です。

書いているように、コードベースはバージョンを認識する必要がありますが、互換性レイヤーを別のレイヤーとして使用できます。あなたの例では、コードベースは姓と名でリソース表現(JSONまたはXML)を生成できますが、互換性レイヤーは代わりに名前のみを持つように変更します。

コードベースは最新バージョンのみを実装する必要があります。v3としましょう。互換性レイヤーは、最新バージョンv3とサポートされているバージョン(v1やv2など)の間で要求と応答を変換する必要があります。互換性レイヤーは、チェーンとして接続できるサポートされているバージョンごとに個別のアダプターを持つことができます。

例えば:

クライアントv1リクエスト:v1がv2に適応---> v2がv3に適応---->コードベース

クライアントv2リクエスト:v1がv2に適応(スキップ)---> v2がv3に適応---->コードベース

応答の場合、アダプターは単に反対方向に機能します。Java EEを使用している場合は、たとえばサーブレットフィルターチェーンをアダプターチェーンとして使用できます。

1つのバージョンの削除は簡単です。対応するアダプターとテストコードを削除します。


基盤となるコードベース全体が変更された場合、互換性を保証することは困難です。バグ修正リリースの古いコードベースを保持する方がはるかに安全です。
Marcelo Cantos

5

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

すでに述べたとおり-バグ修正のバックポートには多少の労力が必要ですが、同時に、1つのソースベースで複数のバージョンをサポートする(ルーティングやその他すべてのものを使用する)場合は、少なくはありませんが、少なくとも同じ努力でシステムをより多くする必要があります内部のロジックの異なるブランチを持つ複雑で巨大な(バージョン管理のある時点で、明確にcase()、コードが重複している、またはさらに悪いバージョンモジュールを指し示すようになります)if(version == 2) then...)。また、リグレッションのために、テストを分岐させておく必要があることも忘れないでください。

バージョン管理ポリシーについて:私は現在のバージョンから最大-2バージョンを維持し、古いバージョンのサポートを廃止します-これはユーザーが移動する動機を与えます。


現在、単一のコードベースでテストすることを考えています。テストは常に分岐する必要があるとおっしゃいましたが、v1、v2、v3などのすべてのテストも同じソリューションで実行でき、すべて同時に実行できると思います。私は、彼らがサポートして何のバージョンを指定した属性を持つテストを飾ると思っている:例えば [Version(From="v1", To="v2")][Version(From="v2", To="v3")][Version(From="v1")] // All versions ちょうど今それを模索し、今までそれを誰にもない聞いたことありますか?
Lee Gunn

1
まあ、3年後、元の質問に対する正確な回答がないことを知りました:D。プロジェクトに非常に依存しています。APIをフリーズして維持するだけの余裕がある場合(例:バグ修正)、関連コード(API関連のビジネスロジック+テスト+残りのエンドポイント)をブランチ/デタッチし、すべてを共有します(独自のテストを使用) )。V1がV2とかなり長い間共存し、機能の作業がまだ進行中の場合、それらをまとめてテストします(V1、V2などをカバーし、それに応じて名前を付けます)。
edmarisov 2018年

1
ありがとう。ええ、それはかなり説得力のあるスペースのようです。まず、1つのソリューションアプローチを試して、それがどのようになるかを確認します。
Lee Gunn、

0

通常、複数のバージョンを維持する必要がある状況であなたを導くAPIのメジャーバージョンの導入は、非常に頻繁に発生しない(または発生してはならない)イベントです。ただし、完全に回避することはできません。メジャーバージョンは、いったん導入されると、比較的長期間にわたって最新バージョンのままであることが、全体として安全な仮定であると思います。これに基づいて、最新のバージョンで変更を導入するときに以前のバージョンを壊さないという自信が得られるため、重複を犠牲にしてコードを単純化することを好みます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.