soap-faultsintegrationobservabilitylegacy-modernization

生の SOAP Fault の解釈を利用側のチームに押し付けない方がいい理由

SOAPless Team8 min read

連携で最もコストが高い習慣のひとつは、生の SOAP Fault をそのまま下流チームに転送し、想定外の XML エラー構造を解析させることです。

利用側のすべての consumer が soap:Clientsoap:Server の違いを理解し、<detail> 要素からベンダー固有の例外文字列を掘り出し、そのエラーがリトライ可能かどうかを判定しなければならないのであれば、連携の簡素化にはなっていません。連携コストを組織全体に複製しているだけです。

SOAP Fault の構造

正規化を議論する前に、生の SOAP Fault が何を含んでいるかを把握します。

SOAP 1.1 の Fault 構造

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Client</faultcode>
      <faultstring>Invalid account number format</faultstring>
      <faultactor>http://example.com/billing</faultactor>
      <detail>
        <ValidationError xmlns="http://example.com/errors">
          <Field>accountNumber</Field>
          <Rule>Must be 10 digits</Rule>
        </ValidationError>
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

SOAP 1.2 の Fault 構造

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <soap12:Fault>
      <soap12:Code>
        <soap12:Value>soap12:Sender</soap12:Value>
        <soap12:Subcode>
          <soap12:Value>ex:ValidationFailed</soap12:Value>
        </soap12:Subcode>
      </soap12:Code>
      <soap12:Reason>
        <soap12:Text xml:lang="en">Invalid account number format</soap12:Text>
      </soap12:Reason>
      <soap12:Detail>
        <ValidationError xmlns="http://example.com/errors">
          <Field>accountNumber</Field>
          <Rule>Must be 10 digits</Rule>
        </ValidationError>
      </soap12:Detail>
    </soap12:Fault>
  </soap12:Body>
</soap12:Envelope>

2 つのバージョンでは要素名、コード体系(Client/Server vs Sender/Receiver)、ネスト構造がすべて異なります。下流チームに両方の対応を求めることは、彼らが望んでいない SOAP パーサーの構築を強いることになります。

Fault をそのまま流すと何が高くつくのか

生の SOAP Fault が下流チームに届くと、各チームが独立して以下を解決しなければなりません。

  1. パース: 構造が異なる XML からエラー情報を抽出
  2. 分類: クライアントエラー(入力誤り)かサーバーエラー(上流障害)かを判定
  3. リトライ判断: soap:Server なら一時的、soap:Client なら恒久的と判断
  4. ユーザー通知: XML の Fault 文字列をユーザーやオペレーターが理解できる形に変換
  5. 監視: ベンダーごとに異なる XML Fault コードに基づくアラートやダッシュボードの構築

同じ SOAP サービスを利用する 3 チームがあれば、3 つの微妙に異なる Fault パーサー、3 つの異なるリトライ戦略、3 つの異なる監視ダッシュボードができ上がります。いずれもほぼ同じ作業です。

Before / After: 正規化でどう変わるか

Before -- 生の SOAP Fault をそのまま転送

consumer は完全な XML body と汎用的な HTTP 500 を受け取ります。

curl -s https://api.internal.example.com/billing/validate \
  -H "Content-Type: text/xml" \
  -d @request.xml

# Response: HTTP 500
# Body: 生の SOAP Fault XML(47 行の XML)

consumer は XML を解析し、<faultcode> を抽出し、<faultstring> を読み、必要に応じて <detail> をたどる必要があります。技術スタックに XML パーサーがないチームもあるでしょう。

After -- 連携境界で正規化された JSON エラー

連携境界が SOAP Fault を構造化された JSON レスポンスに変換し、適切な HTTP ステータスコードを付与します。

{
  "error": {
    "type": "validation_error",
    "code": "INVALID_FORMAT",
    "message": "Invalid account number format",
    "detail": {
      "field": "accountNumber",
      "rule": "Must be 10 digits"
    },
    "upstream": {
      "soap_fault_code": "soap:Client",
      "soap_fault_actor": "http://example.com/billing"
    }
  }
}

この方式のメリット:

  • HTTP ステータス でエラー種別が分かる: クライアント Fault なら 400、サーバー Fault なら 502
  • 構造化 JSON はどの言語でも XML 依存なしに解析可能
  • 上流メタデータ はデバッグ用に保持されつつ、生の XML が全 consumer に漏れない
  • リトライ判断 が明快: 400 = リトライ不要、502 = バックオフ付きリトライ

SOAP Fault コードから HTTP ステータスへのマッピング

信頼できる正規化レイヤーは Fault コードに基づいて適切な HTTP ステータスを選びます。

SOAP Fault コード意味HTTP ステータス
soap:Client / Sender呼び出し側の入力誤り400
soap:Server / Receiver上流サービスの障害502
soap:VersionMismatchSOAP バージョン不一致400
soap:MustUnderstand必須ヘッダー未処理400
タイムアウト / 無応答上流が応答しない504

責任ある境界が行う 3 つのこと

健全な連携境界は以下の 3 つの機能を果たします。

  1. Fault 形式の正規化: どの上流 SOAP サービスが生成したエラーでも、同一の JSON スキーマに従う
  2. 診断情報の保持: 元の Fault コードと Fault 文字列はデバッグ用に利用可能だが、XML 解析を必要としない構造でラップされている
  3. XML 処理の遮断: フロントエンド開発者、モバイルエンジニア、データパイプラインチームがエラー処理のために XML ライブラリを必要としない

これは情報を隠すことではありません。SOAP を学ばなくても組織全体が行動できる形に Fault を翻訳することです。

誰も計測しないコミュニケーションコスト

コードの問題だけではありません。生の SOAP Fault は目に見えないコミュニケーションの負荷を生みます。

  • Slack のスレッド: 「soap:Server って何ですか? リトライしていいですか?」
  • インシデント対応: 「こちらのバグですか、上流の障害ですか?」 -- 回答するには XML を読む必要がある
  • オンボーディング: 新しいエンジニアが連携エラーをデバッグするには、まず SOAP Fault のセマンティクスを学ぶ必要がある
  • ドキュメント: 各チームが独自に「SOAP Fault の読み方」ガイドを作成する

境界で Fault を正規化すれば、これらの会話は不要になります。明確なメッセージ付きの 400 は誰でも理解できます。

SOAPless による自動処理

SOAPless はまさにこの連携境界に位置します。SOAP サービスが Fault を返した場合、SOAPless は自動的に以下を行います。

  • SOAP Fault XML を解析(1.1 と 1.2 の両形式に対応)
  • Fault コードを適切な HTTP ステータスコードにマッピング
  • 元の Fault 詳細を保持した正規化 JSON エラーレスポンスを返却
  • Fault 情報をダッシュボードでデバッグ用に可視化

下流の consumer は標準的な HTTP ステータスコード付きのクリーンな JSON エラーを受け取ります。XML の解析も、SOAP Fault の分類体系の学習も、独自の正規化レイヤーの構築も不要です。SOAP の複雑さは、あるべき場所 -- 境界 -- にとどまります。