私たちは皆、ピカピカの新しいツールを持つのが大好きですが、常に更新するという面倒な作業を嫌います。これは、オペレーティング システム、アプリ、API、Linux パッケージなど、あらゆるものに当てはまります。更新によってコードが機能しなくなるのは苦痛ですが、更新が私たちによって開始されていない場合はその苦痛の 2 倍になります。
Web API 開発では、新しい更新が行われるたびにユーザーのコードが破損する危険に常にさらされています。あなたの製品が API である場合、これらのアップデートは毎回恐ろしいものになります。 Monite の主な製品は、API とホワイトラベル SDK です。当社は API ファーストの企業であるため、API を安定して使いやすく保つことに細心の注意を払っています。したがって、重大な変更の問題は、優先順位リストの最上位に近いものとなります。
一般的な解決策は、クライアントに非推奨の警告を発行し、重大な変更はほとんどリリースしないことです。突然、リリースに数か月かかる場合があり、一部の機能は次のリリースまで非表示にしておくか、マージしないようにしなければなりません。これにより開発が遅くなり、ユーザーは数か月ごとに統合を更新する必要があります。
リリースを速くすると、ユーザーは統合を頻繁に更新しなければならなくなります。リリース間の期間が長くなると、企業としての動きが遅くなります。ユーザーにとって不便になればなるほど、あなたにとっても便利になりますし、その逆も同様です。これは確かに最適なシナリオではありません。私たちは、既存のクライアントに何も影響を与えることなく、自分たちのペースで進めたいと考えていました。これは、通常の非推奨アプローチでは不可能でした。これが、代替ソリューションである API バージョン管理.
を選択した理由です。これは非常に単純なアイデアです。重大な変更はいつでもリリースしますが、新しい API バージョンの下では非表示にします。これにより、両方の長所が得られます。ユーザーは統合が日常的に中断されることがなくなり、好きな速度で移動できるようになります。ユーザーは、プレッシャーを感じることなく、いつでも好きなときに移行できます。
アイデアのシンプルさを考慮すると、どの企業にも最適だと思われます。これは、典型的なエンジニアリング ブログで期待される内容です。残念ながら、それはそれほど単純ではありません。
API のバージョン管理は非常に困難です。その幻想的な単純さは、実装を開始するとすぐに消えてしまいます。残念なことに、このトピックに関するリソースは驚くほど少ないため、インターネット上では実際に警告されることはありません。圧倒的多数の記事は API バージョンをどこに配置するかについて議論していますが、「どうやって API を実装するか?」という質問に答えようとしている記事はほとんどありません。最も一般的なものは次のとおりです:
個別のデプロイメントは非常に高価でサポートが困難になる可能性があり、単一のルートのコピーは大規模な変更にあまり対応できません。また、アプリケーション全体をコピーすると余分なコードが大量に作成されるため、ほんの数バージョンでコードに溺れてしまいます。
最も安いものを選択しようとしても、バージョン管理の負担がすぐに追いついてしまいます。最初は単純に感じるでしょう。ここに別のスキーマを追加し、そこにビジネス ロジックの別のブランチを追加し、最後にいくつかのルートを複製します。しかし、十分なバージョンが与えられると、ビジネス ロジックはすぐに管理できなくなり、多くの開発者がアプリケーションのバージョンと API のバージョンを間違えて、データベース内のデータのバージョン管理を開始し、アプリケーションを保守できなくなります。
同時に 2 つまたは 3 つ以上の API バージョンを使用しないことを望むかもしれません。古いバージョンは数か月ごとに削除できるようになります。これは、少数の内部消費者のみをサポートする場合に当てはまります。ただし、組織外のクライアントは、数か月ごとにアップグレードを強制される経験を楽しむことはできません。
API のバージョン管理は、すぐにインフラストラクチャの最も高価な部分の 1 つになる可能性があるため、事前に入念な調査を行うことが重要です。内部コンシューマーのみをサポートする場合は、GraphQL などを使用すると簡単に済むかもしれませんが、すぐにバージョン管理と同じくらいコストが高くなる可能性があります。
スタートアップの場合は、API のバージョン管理を、適切に行うためのリソースが確保される開発の後期段階まで延期することが賢明です。それまでは、非推奨化と追加的な変更戦略で十分かもしれません。 API の見栄えが必ずしも優れているとは限りませんが、明示的なバージョニングを避けることで、少なくとも大幅なコストを節約できます。
数回の試行と多くのエラーの後、私たちは岐路に立たされました。上で述べた以前のバージョン管理アプローチは維持するにはコストがかかりすぎました。苦闘の結果、私は完璧なバージョニング フレームワークに必要な次の要件リストを考案しました。
残念ながら、既存のアプローチに代わる選択肢はほとんどありませんでした。このとき、クレイジーなアイデアが頭に浮かびました。洗練されたもの、仕事に最適なもの、つまり Stripe の API バージョン管理のようなものを構築してみたらどうなるでしょうか?
数え切れないほどの実験の結果、現在は Cadwyn を使用しています。これは、Stripe のアプローチを実装するだけでなく、その上に大幅に構築されたオープンソースの API バージョン管理フレームワークです。 Fastapi と Pydantic の実装について説明しますが、中心となる原則は言語とフレームワークに依存しません。
他のすべてのバージョン管理アプローチの問題は、重複が多すぎることです。コントラクトのごく一部が壊れているだけなのに、なぜルート、コントローラー、さらにはアプリケーション全体を複製するのでしょうか?
Cadwyn を使用すると、API メンテナが新しいバージョンを作成する必要があるたびに、最新のスキーマ、モデル、ビジネス ロジックに重大な変更を適用できます。次に、バージョン変更、つまり新しいバージョンと以前のバージョンのすべての違いをカプセル化するクラスを作成します。
たとえば、以前はクライアントがアドレスを持つユーザーを作成できましたが、今後は単一のアドレスではなく複数のアドレスを指定できるようにしたいとします。バージョンの変更は次のようになります:
class ChangeUserAddressToAList(VersionChange): description = ( "Renamed `User.address` to `User.addresses` and " "changed its type to an array of strings" ) instructions_to_migrate_to_previous_version = ( schema(User).field("addresses").didnt_exist, schema(User).field("address").existed_as(type=str), ) @convert_request_to_next_version_for(UserCreateRequest) def change_address_to_multiple_items(request): request.body["addresses"] = [request.body.pop("address")] @convert_response_to_previous_version_for(UserResource) def change_addresses_to_single_item(response): response.body["address"] = response.body.pop("addresses")[0]
instructions_to_merge_to_previous_version は、古い API バージョンのスキーマ用のコードを生成するために Cadwyn によって使用されます。2 つのコンバータ関数は、必要な数のバージョンを維持できるようにするためのトリックです。プロセスは次のようになります:
API メンテナがバージョン変更を作成した後、それを VersionBundle に追加して、この VersionChange が一部のバージョンに含まれることを Cadwyn に伝える必要があります。
VersionBundle( Version( date(2023, 4, 27), ChangeUserAddressToAList ), Version( date(2023, 4, 12), CollapseUserAvatarInfoIntoAnID, MakeUserSurnameRequired, ), Version(date(2023, 3, 15)), )
以上です。重大な変更を追加しましたが、ビジネス ロジックは 1 つのバージョン (最新) のみを処理します。数十の API バージョンを追加した後でも、ビジネス ロジックにはバージョン管理ロジック、定期的な名前変更、if およびデータ コンバーターが必要ありません。
バージョンの変更は API のパブリック インターフェースに依存しており、既存の API バージョンに重大な変更を追加することはほとんどありません。これは、一度リリースしたバージョンは壊れないことを意味します。
バージョンの変更はバージョン内の重大な変更を記述し、古いバージョンには重大な変更がないため、バージョンの変更は完全に不変であると確信できます。変更する理由はありません。不変エンティティは常に進化するため、ビジネス ロジックの一部である場合よりも保守がはるかに簡単です。バージョンの変更も次々に適用され、バージョン間にトランスフォーマーのチェーンが形成され、リクエストを新しいバージョンに移行したり、レスポンスを古いバージョンに移行したりできます。
API コントラクトは、単なるスキーマやフィールドよりもはるかに複雑です。これらは、すべてのエンドポイント、ステータス コード、エラー、エラーメッセージ、さらにはビジネス ロジックの動作で構成されます。 Cadwyn は、上で説明したのと同じ DSL を使用してエンドポイントとステータス コードを処理しますが、エラーとビジネス ロジックの動作は別の話です。DSL を使用して記述することは不可能であり、ビジネス ロジックに埋め込む必要があります。
This makes such version changes much more expensive to maintain than all others because they affect business logic. We call this property a "side effect" and we try to avoid them at all costs because of their maintenance burden. All version changes that want to modify business logic will need to be marked as having side effects. It will serve as a way to know which version changes are "dangerous":
class RequireCompanyAttachedForPayment(VersionChangeWithSideEffects): description = ( "User must now have a company_id in their account " "if they want to make new payments" )
It will also allow API maintainers to check that the client request uses an API version that includes this side effect:
if RequireCompanyToBeAttachedForPayment.is_applied: validate_company_id_is_attached(user)
Cadwyn has many benefits: It greatly reduces the burden on our developers and can be integrated into our infrastructure to automatically generate the changelog and improve our API docs.
However, the burden of versioning still exists and even a sophisticated framework is not a silver bullet. We do our best to only use API versioning when absolutely necessary. We also try to make our API correct on the first try by having a special "API Council". All significant API changes are reviewed there by our best developers, testers, and tech writers before any implementation gets moving.
Special thanks to Brandur Leach for his API versioning article at Stripe and for the help he extended to me when I implemented Cadwyn: it would not be possible without his help.
以上がMonite での API のバージョニングの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。