変更のための先見の明を持つ外部アプリケーション用のAPIを設計しようとすることは簡単ではありませんが、少し前もって考えておくことで、後で生活が楽になります。私は、以前のバージョンのハンドラーをそのままにしておくことで、後方互換性を維持しながら将来の変更をサポートするスキームを確立しようとしています。
この記事の主な関心事は、特定の製品/会社に対して定義されたすべてのエンドポイントでどのパターンに従う必要があるかです。
基本スキーム
のベースURLテンプレートを考えると、https://rest.product.com/
すべてのサービスが/api
、/auth
およびなどの他の非レストベースのエンドポイントと共に存在することを考案しました/doc
。したがって、次のようにベースエンドポイントを確立できます。
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
サービスエンドポイント
次に、エンドポイント自体について説明します。懸念はPOST
、GET
、DELETE
この記事の主な目的ではなく、それらのアクション自体に懸念されます。
エンドポイントは、名前空間とアクションに分類できます。また、各アクションは、戻り値の型または必須パラメーターの基本的な変更をサポートする方法で表示される必要があります。
登録されたユーザーがメッセージを送信できる架空のチャットサービスを利用すると、次のエンドポイントがある場合があります。
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
今度は、壊れる可能性のある将来のAPI変更に対するバージョンサポートを追加します。私たちは、どちらかの後にバージョンの署名を追加することができ/api/
たりした後/messages/
。与えられたsend
エンドポイント我々は、v1の以下のものを持つことができます。
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
だから私の最初の質問は、バージョン識別子の推奨場所は何ですか?
コントローラーコードの管理
そのため、以前のバージョンをサポートする必要があることを確立しました。そのため、時間の経過とともに廃止される可能性のある新しいバージョンのそれぞれのコードを何らかの方法で処理する必要があります。Javaでエンドポイントを記述していると仮定すると、これをパッケージで管理できます。
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
これには、すべてのコードがネームスペースを介して分離されているという利点があります。この場合、重大な変更はサービスエンドポイントの新しいコピーを意味します。これの不利な点は、すべてのコードをコピーする必要があり、新しいバージョンと以前のバージョンに適用するバグ修正を各コピーに適用/テストする必要があることです。
別のアプローチは、エンドポイントごとにハンドラーを作成することです。
package com.product.messages;
public class MessageServiceImpl {
public void send(String version) {
getMessageSender(version).send();
}
// Assume we have a List of senders in order of newest to oldest.
private MessageSender getMessageSender(String version) {
for (MessageSender s : senders) {
if (s.supportsVersion(version)) {
return s;
}
}
}
}
これにより、バージョン管理が各エンドポイント自体に分離され、ほとんどの場合一度適用するだけでバグ修正とポート互換性が保たれますが、これをサポートするには個々のエンドポイントにもう少し作業を行う必要があります。
そこで、2つ目の質問「RESTサービスコードを設計して以前のバージョンをサポートするための最良の方法は何か」があります。