search
LoginSignup
0

posted at

APIのバージョン管理どうしてる?

はじめに

去年までFintechな企業でマイクロサービス化とAPI周りの設計をやっていたのですが、最近他業種でも「APIを始めたいんだよね」と言った声をよく聞くようになってきました。API(WebAPI)自体はもう十分枯れた領域で特別なことはないのですが、今からAPIを始める方のために実際にAPIを作った際の話をまとめてみます。今回は資産管理(バージョニング)についての話から

目次

  1. APIのバージョニング
  2. 参考文献

APIのバージョニング

APIの役割はシステム間の統合(インテグレーション)にあります。APIを構築したのち、さまざまなサービスからAPIを呼び出していると用件に応じてインターフェースに差異が生まれることがあります。APIの多くは公開し続けながらアップデートを行なっていく必要があり、相手はWebの先にいるため従来のスタンドアロンなシステムのように全体を透過的にリファクタリングするだけでは対応ができないことも多々あります。たとえば

UserAPI.yaml
paths:
  /user:
    post:
      summary: "顧客情報更新"
      description: "指定されたユーザの情報を更新します"
      parameters:
      - id: "Id"
        name: "名無しさん"
        age: "20"
        address: ""

このようなリクエストでAとBとCのサービスから呼ばれていたUserAPIがあるったとして、Cのサービスのアップデートの影響でUserAPIでも電話番号の管理が必要になったとします。Bのサービスのために、UserAPIに以下のように項目を追加してしまうと、AとCのサービスからの問い合わせ時にリクエストパラメータの不足による不整合が生じてしまうかもしれません。

UserAPI.yaml
paths:
  /user:
    post:
      summary: "顧客情報更新"
      description: "指定されたユーザの情報を更新します"
      parameters:
      - id: "Id"
        name: "名無しさん"
        age: "20"
        address: ""
        tel: "000-0000-0000"

ここでバージョン化を検討し始めます。AとBには古いバージョンのAPIを呼び出してもらい、Cには新しいバージョンのAPIを呼び出してもらうことでこの差を解消していこうと考えたりします。RESTfulなAPIだと、たとえば以下のようにバージョニング表記ができます。

  1. パスベースでの解決だと一般的にはこんな感じ。
    /user/v1/
  2. もう1つはクエリストリングに追加する。
    /user/?v1
  3. リクエストパラメータにバージョンを追加するようにする。
parameters:
     ver: 1.1.0

など。

この中で個人的に一番勝手が良かったのはクエリストリングに追加する方法でした。一般的にはパスベースが選択されるケースが多いのではないでしょうか。ちなみに最初はリクエストパラメータで管理をしていたのですが、ログを見てもどのバージョンが呼ばれたのかわからず視認性が良くないです。ついでにリクエストは利用側の入力フォームでの制御として呼び出し側の仕掛け(POST前にVerのパラメータを追加するなど)が必要で大変不評でした。パスベースだと設定をミスると呼び出しが成功しないのもマイグレーション時には不便だったりしました。クエリに預けておけばアプリまでは到達してくれるのがポイント高かったです(個人の意見)

バージョンを用意すると今度はプログラム側で分岐判定が発生するので、クリーンアーキテクチャのようにインターフェースを完全に分離しておく必要があります。しかしインターフェースでバージョンを吸収していると、今度は次第にインターフェースが肥大化してしんどくなります。

上記のような話からAPI設計・実装する際は後方互換を検討しておくのがセオリーですが、私は以下のように対応してみました。

    @RequestMapping(path = "/user", params = "v2", method = RequestMethod.POST)
    public List<String> updateUser(@RequestParam("id") String userId, @RequestParam("tel") String tel) {
        // アプリケーションの本処理へ
    }

    @RequestMapping(path = "/user", params = "v1", method = RequestMethod.POST)
    public List<String> updateUserV1(@RequestParam("id") String userId) {
        // telを擬似的に作成するなどして処理する
        updateUser(userId, tel) 
    }

古いAPIからのリクエストを実装側で新しいAPIのリクエストに整形して呼び出すようにして、秘伝のタレみたいに継ぎ足していました。あまり設計上よろしくないのですが、リクエストがなくなった後に古いバージョンのAPIをきれいに捨てられるので管理は非常に楽になりました。

今回は実体験からの話ですので、参考にならない部分も多いのですが、もっと詳しく学びたい方は
Web API: The Good Parts」なんかを読んでみるといいのではないでしょうか。

参考文献

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0