連携で最もコストが高い習慣のひとつは、生の SOAP Fault をそのまま下流チームに転送し、想定外の XML エラー構造を解析させることです。
利用側のすべての consumer が soap:Client と soap: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 が下流チームに届くと、各チームが独立して以下を解決しなければなりません。
- パース: 構造が異なる XML からエラー情報を抽出
- 分類: クライアントエラー(入力誤り)かサーバーエラー(上流障害)かを判定
- リトライ判断:
soap:Serverなら一時的、soap:Clientなら恒久的と判断 - ユーザー通知: XML の Fault 文字列をユーザーやオペレーターが理解できる形に変換
- 監視: ベンダーごとに異なる 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:VersionMismatch | SOAP バージョン不一致 | 400 |
soap:MustUnderstand | 必須ヘッダー未処理 | 400 |
| タイムアウト / 無応答 | 上流が応答しない | 504 |
責任ある境界が行う 3 つのこと
健全な連携境界は以下の 3 つの機能を果たします。
- Fault 形式の正規化: どの上流 SOAP サービスが生成したエラーでも、同一の JSON スキーマに従う
- 診断情報の保持: 元の Fault コードと Fault 文字列はデバッグ用に利用可能だが、XML 解析を必要としない構造でラップされている
- 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 の複雑さは、あるべき場所 -- 境界 -- にとどまります。