VOLUME 02 / CHAPTER 03

接続できることと、契約が合うこと

APIの型、データの意味、結果、時間、変更の互換性を分ける。HTTPの性質を、予約という業務操作の保証へ正しく接続する。

出典照合日: · 独自解説

架空事例:青葉機材と外部サービスのインタフェースを設計する。項目名や応答例は学習用で、特定の製品APIの実際の仕様を表していない。

同じ整数を、違う単位で読む

予約システムは、外部の料金サービスへ利用期間として「5」を送った。外部サービスは金額を返した。通信は成功し、JSONも正しく解析できた。ところが、営業が期待した金額より大幅に安い。

予約側は五日、料金側は五時間と解釈していた。

型が一致することは、意味が一致することの一部にすぎない。整数か文字列か、必須か省略可能かを定めても、単位、基準時点、丸め方、税や割引を含むかが異なれば、業務上の結果は合わない。シラバス10-2が要求する矛盾のないインタフェース設計は、接続の成功だけでは確かめられない。1

同じことは、「終了日」「在庫数」「確定」という項目にも起きる。終了日が利用最終日なのか、返却が完了する日なのか。在庫数が保有数なのか、指定期間に使える数量なのか。確定が予約の成立なのか、入力の受付なのか。名前の一致を、契約の一致として扱わない。

項目一覧に、正常な値の意味を足す

青葉機材では、利用期間を開始時刻と終了時刻で渡す案を考える。時刻の表現を決めても、どの時間帯を基準とし、終了時刻を区間に含めるかを合わせなければ、境界で一日分の料金差が出る可能性がある。

値がない場合も定義する。返却予定が未定なのか、返却不要なのか、入力が漏れているのか。これらを同じ空欄にすると、受信側が区別できない。NULL、空文字列、ゼロ、省略を、理由なく同じものとして処理しない。

さらに、要求した値がそのまま採用されるとは限らない。五台を依頼し、三台だけ確保できたとき、部分成立を許すか。受信側が数量を三へ補正して成功を返すなら、呼出し元はその結果を顧客へ提示して合意し直す必要があるかもしれない。自動補正は便利だが、何を合意した操作だったかを変えることがある。

本事例では、標準予約は要求した機種・数量・期間がすべて成立した場合だけ確定とする。代替案がある場合は、確定結果とは別に提示する。これは青葉機材が選んだ業務契約であり、すべての予約システムに共通する規則ではない。

冪等であることと、同じ応答を返すこと

HTTPでは、安全なメソッドと、冪等なメソッドを区別する。RFC 9110では、冪等性を、同じ要求を複数回行ったときの意図された効果が一回の場合と同じである性質としている。PUT、DELETE、安全なメソッドは、この分類で冪等である。2

「削除」を一度実行した後、同じ削除を繰り返した場面を考える。一回目は削除成功、二回目は対象が既に存在しないという応答になっても、対象を存在しない状態にするという効果は増えていない。応答のコードや本文が毎回同じでなければ冪等でない、とはいえない。

反対に、どの要求にも同じ「成功」を返すだけでは、冪等性を満たした証拠にならない。裏側で毎回予約を追加しているなら、副作用は重複している。評価するのは、応答文字列の同一性ではなく、約束した操作の効果である。

GETのような安全なメソッドにも、アクセスログの記録などが伴い得る。安全性は、利用者が意図した状態変更を要求する操作かという観点であり、実装の内部で一切の変化が起きないという意味ではない。2 だからといって、GETで予約を確定してよいという結論にはならない。メソッド名を付けただけで、誤った実装が安全になるわけでもない。

受付に成功しても、業務は終わっていない

時間のかかる予約処理を非同期にし、受付直後にHTTP 202を返す案が出た。202 Acceptedは、処理を受け付けたが完了していないことを表す。最終的に処理が実行・成立する保証を、そのステータス自体が与えるわけではない。2

営業へ見せる画面には、「受付番号を取得した」と「予約が確定した」を分けて表示する必要がある。結果を確認する操作、未完了の期限、失敗時の引渡し先を定める。単にHTTPのコードを成功系にしたから、顧客へ確約できる状態になるのではない。

このとき、受付番号を返す前に何を保存するかも重要になる。受付を記録せずに番号だけ返した後、サーバが停止したら、その番号で照会しても記録がない。利用者は受け付けられたと理解しているが、システムは追跡できない。この不整合を避けるため、受付成功として何を保証するかを先に決める。

APIの契約には、正常な結果の形だけでなく、処理中、結果不明、不成立、再照会可能という状態も含める。利用者へ次の行動を選ばせるのに必要な区別だからである。

古い画面からの更新を、どう扱うか

二人の営業担当者が、同じ予約の版を開いた。一人が数量を三台へ変更する。その後、もう一人が利用期間だけを変えたつもりで、画面に残る古い数量二台を含めて保存した。最初の更新は失われたように見える。

表示した時点の情報を前提に変更するなら、その前提が今も成立するかを確認する必要がある。HTTPでは、ETagとIf-Matchを使って、対象の表現が指定した条件に合う場合だけ処理する仕組みがある。If-Matchには強い比較を用い、条件が満たされないときの処理を規定している。2

本事例では、予約の版を照合し、変わっていた場合は最新の内容を確認してから変更し直す案を考える。ただし、版を一度読んで一致を確認し、その後に無条件で更新するだけでは、確認と更新の間に別の変更が入る可能性がある。前提の確認と更新を、競合に耐える実装へ結び付ける必要がある。

また、一つの予約の版を確認しても、別の予約との機材確保量まで自動的に正しくなるわけではない。同じ期間の他予約を含む制約は、別に守らなければならない。HTTPの条件付き要求は、任意の業務不変条件を代わりに設計する仕組みではない。

項目を追加するだけなら、互換性は壊れないか

予約状態は、確認中、確定、取消しの三種類だった。新しい版では、出庫準備中を追加した。サーバ側は文字列を一つ増やしただけである。ところが、古いクライアントは三種類をすべて列挙し、それ以外を異常としていた。追加しただけなのに動かなくなる。

互換性は、受信できるデータ構造だけでなく、既存の利用側がどの振る舞いを期待しているかに関わる。GoogleのAPI設計指針AIP-180も、既存の利用者を壊さない変更と、意味や既定の動作を変える変更を区別している。ただし、これはGoogleのAPI指針であり、全APIに同じ変更規則を法的・規格的に要求するものではない。3

新しい任意項目を追加しても、未知の項目を拒否する利用側があるかもしれない。省略時の既定値を変えれば、同じ要求でも結果が変わる。応答の順序が契約に含まれているのに変えれば、型が同じでも互換性を損なう。どこまでを約束していたかを確認する。

本事例では、利用側の更新時期が揃わないため、旧版が受け付ける状態の範囲を保つ窓口を残し、新しい状態を扱える利用側から移行する案を比較する。旧版への変換で意味を失うなら、その制限も説明する。「旧版互換」と表示するだけで、すべての新機能を同じ意味で使えるわけではない。

一方、互換性のために古い方式を永久に残すと、運用や検証の範囲が増える。移行期限、利用状況の確認、終了時の対応を合意することも、インタフェースのライフサイクルに含まれる。

スキーマの検査だけでは、時間の契約は見えない

要求と応答の型を自動検査すれば、必須項目の欠落や型の違いを検出しやすい。しかし、二秒以内に返るか、再送で二重処理しないか、古い版を上書きしないかは、型の検査だけでは確かめられない。

青葉機材では、要求の値、業務上の結果、処理の時点、同時実行、失敗後の状態を分けて確認する。旧版の利用側を含む検証では、実際に旧版が利用する項目や状態を使う。新しいサーバと新しいクライアントだけで成功しても、既存の利用者が壊れないことの証拠にはならない。

契約書のような大きな文書を毎回作る必要はない。ただし、外へ出した振る舞いが何を約束しているかは、相手が実装・運用・変更を判断できる形で残す。原教材が繰り返す「何を保証し、何を保証しないか」は、APIにもそのまま当てはまる。4

接続できたという事実は、入口を通過したことを示す。正しい契約で仕事を終えたことまでは示さない。通信、データ、意味、時点、変更を別々に照合して初めて、二つのシステムは同じ業務を行える。

知識を答案へ戻す

午前Ⅱ。 HTTPの安全性、冪等性、処理受付、処理完了を混同しない。RPCやAPIという名称だけで、業務上の一回性や整合性が保証されるわけではない。応答コードの意味は規格、予約成立の意味は業務契約という違いを読む。

午後Ⅰ。 連携する両者で、単位、状態、更新時点、責任が一致しているかを確認する。問題文で「受信済み」と「登録済み」が分かれていれば、その境界を答案でも消さない。

午後Ⅱ。 互換性を維持したと書くなら、誰のどの利用方法を維持し、何をいつ移行したかを具体化する。新旧を並行させた事実だけでなく、その期間のデータと状態をどう対応付けたかを説明する。

立ち止まって考える:同じ削除要求に、最初は成功、次は不存在を返した。冪等でないのか

応答の違いだけでは判断できない。対象を存在しない状態にするという意図された効果が、一回と複数回で同じかを見る。反対に、同じ成功メッセージを返しても、副作用が毎回増えるなら冪等とはいえない。

出典と照合範囲

[1] IPA「SAシラバス Ver.5.2」:4-9、5-4、9-2、10-2。連携、機能要件、一貫性、インタフェースの方式設計を照合した。公式PDF

[2] RFC 9110, HTTP Semantics:9.2の安全性・冪等性、13.1.1のIf-Match、15.3.3の202を参照した。予約の業務条件や排他制御の方式はRFCが規定するものではなく、本書の例である。RFC本文

[3] Google AIP-180, Backwards compatibility:API変更の互換性を、既存の利用者と振る舞いから検討する観点を参照した。公式設計指針

[4] コンプリートテキスト第2・7・18・19・20章:追跡可能性、再試行、同時実行、HTTP、認証連携を基礎とした。単位の不一致、予約状態の追加、利用期間変更の例は本章独自の設定である。