外部SaaS(決済、CRM、メール配信、認証基盤、チャット等)と自社システムを連携するときの設計・実装・運用の指針をまとめる。


目次

  1. 基本原則
  2. アーキテクチャ設計
  3. 認証・認可とシークレット管理
  4. APIクライアントの実装
  5. Webhook受信
  6. データ同期
  7. エラーハンドリングと障害対応
  8. セキュリティとコンプライアンス
  9. 可観測性(ログ・メトリクス・トレース)
  10. テスト戦略
  11. バージョン管理と変更追従
  12. ベンダー選定とロックイン対策
  13. 運用
  14. チェックリスト

1. 基本原則

  • 外部は必ず失敗する前提で設計する。 ネットワーク断、タイムアウト、レート制限、仕様変更、障害は日常的に起きる。
  • 外部障害を自社サービスの全面障害にしない。 連携機能が止まっても、コア機能は動き続ける(グレースフル・デグラデーション)。
  • 外部のデータモデルを自社ドメインに持ち込まない。 SaaS固有の概念は境界で変換する。
  • すべての書き込み操作は冪等に。 再試行・重複配信が起きても結果が変わらないようにする。
  • 自社が「正」とするデータの所在(Source of Truth)を項目ごとに明確にする。
  • 最小権限。 必要なスコープ・権限だけを要求する。

2. アーキテクチャ設計

2.1 腐敗防止層(Anti-Corruption Layer)を置く

SaaS SDK や API を業務ロジックから直接呼ばない。アダプタ層を挟み、自社のインターフェースで抽象化する。

[業務ロジック] → [自社インターフェース (PaymentGateway 等)] → [Stripeアダプタ] → Stripe API
                                                          └→ [別ベンダーアダプタ]
  • インターフェースは「自社が必要な操作」で定義する(SaaSのAPIをそのまま写さない)。
  • SaaS固有のID・ステータス・エラーコードはアダプタ内で自社の型に変換する。
  • 差し替え・テスト用モックが容易になる。

2.2 同期呼び出しと非同期処理を使い分ける

ケース方式
ユーザーが結果を即座に必要とする(決済承認、ログイン)同期呼び出し+短いタイムアウト
結果が後でよい(CRM登録、通知送信、分析連携)キュー経由の非同期処理
大量データの送受信バッチ/バルクAPI
SaaS側のイベント起点Webhook受信 → キュー投入

ユーザーのリクエスト処理パスから外部呼び出しをできるだけ外すと、レイテンシと障害影響が小さくなる。

2.3 Outboxパターン

DB更新と外部API呼び出しを一貫させたいときは、同一トランザクションで「送信予定イベント」をOutboxテーブルに書き、別ワーカーが送信する。これによりDBコミット済みなのに外部送信されない/その逆、という不整合を防ぐ。

2.4 IDマッピング

  • 自社IDと外部IDの対応表を持つ(例:user_id ↔ stripe_customer_id)。
  • 外部側に自社IDをメタデータとして保存できるなら保存する(突合が楽になる)。
  • 外部IDを自社の主キーにしない。

3. 認証・認可とシークレット管理

3.1 認証方式の選択

方式用途注意点
APIキーサーバー間、自社アカウント漏洩時の影響が大きい。権限を絞ったキーを発行
OAuth 2.0 (Authorization Code + PKCE)ユーザーの代理でアクセスリフレッシュトークンの安全な保管と更新処理
OAuth 2.0 (Client Credentials)サーバー間トークンのキャッシュと期限前更新
JWT / 署名付きリクエスト高セキュリティ連携時刻同期、鍵ローテーション
mTLS金融・社内基幹証明書の期限管理

3.2 シークレット管理

  • シークレットはコード・リポジトリ・ログに含めない。Secrets Manager / Vault 等で管理する。
  • 環境(本番/ステージング/開発)ごとに別キーを使う。本番キーを開発環境に置かない。
  • 定期ローテーションの手順を用意し、新旧キーを並行稼働できる設計にする。
  • 漏洩時の失効手順を Runbook に書いておく。
  • 可能なら IP 制限・スコープ制限・読み取り専用キーを活用する。

3.3 OAuthトークン運用

  • アクセストークンは期限前に更新する(期限切れエラーを待たない)。
  • リフレッシュトークンは暗号化して保存する。
  • 複数ワーカーが同時に更新して競合しないよう、ロックをかける(ローテーション型リフレッシュトークンでは特に重要)。
  • ユーザーが連携を解除・権限取消した場合(invalid_grant 等)を検知し、再連携を促すUIフローを用意する。
  • 要求スコープは最小限にし、追加機能が必要になったら段階的に追加同意を取る。

4. APIクライアントの実装

4.1 タイムアウト

  • すべての外部呼び出しにタイムアウトを設定する。 デフォルト無制限のライブラリは多い。
  • 接続タイムアウト(短め:1〜3秒)と読み取りタイムアウト(処理に応じて)を分ける。
  • 上流のリクエスト全体の締め切りから逆算して設定する。

4.2 リトライ

  • 指数バックオフ+ジッターを使う(例:min(cap, base * 2^n) * random(0.5, 1.0))。
  • リトライ対象を限定する:
    • リトライする:ネットワークエラー、タイムアウト、429、502、503、504
    • リトライしない:400、401、403、404、422 などクライアント起因のエラー
  • Retry-After ヘッダーがあれば従う。
  • 最大試行回数・総時間に上限を設ける。
  • 非冪等な操作(POST)は冪等キーなしでリトライしない。

4.3 冪等性

  • SaaSが冪等キー(Idempotency-Key ヘッダー等)をサポートしていれば必ず使う。
  • キーは業務上の一意な操作に紐づけて生成する(例:order-{order_id}-charge)。リトライ毎に新規生成しない。
  • サポートがない場合は、送信前に外部側の存在確認をする、または自社側で送信済み記録を持つ。

4.4 レート制限

  • SaaSの制限値(リクエスト/秒、日次上限、同時接続数)をドキュメントで確認し、自社側でもスロットリングする。
  • レスポンスヘッダー(X-RateLimit-Remaining 等)を監視する。
  • 複数プロセスから呼ぶ場合は、共有のレートリミッター(Redis等)で全体を制御する。
  • バルクAPIがあれば優先して使う。

4.5 サーキットブレーカー

  • 連続失敗が閾値を超えたら一定時間呼び出しを止め、即座にフォールバックする。
  • 障害中のSaaSに大量リトライを送り続けて、自社のスレッド・コネクションを枯渇させるのを防ぐ。
  • バルクヘッド(連携先ごとにコネクションプール/ワーカーを分離)も併用する。

4.6 ページネーション

  • カーソル方式がある場合はオフセット方式より優先する(取得中のデータ変動に強い)。
  • 全件取得ループには上限と中断・再開の仕組みを持たせる。

4.7 SDKの扱い

  • 公式SDKは便利だが、バージョンを固定し、更新は変更履歴を確認してから行う。
  • SDKのデフォルト設定(タイムアウト、リトライ、ログ出力)を確認・上書きする。
  • SDKの型をアダプタ層の外に漏らさない。

5. Webhook受信

5.1 受信処理の基本形

受信 → 署名検証 → 生データ保存 → キュー投入 → 即 2xx 返却
                                      ↓
                              ワーカーで非同期処理(冪等)
  • すぐに2xxを返す。 重い処理を同期で行うとタイムアウト→再送の連鎖が起きる。
  • 処理はキュー経由で非同期に行う。

5.2 セキュリティ

  • 署名を必ず検証する(HMAC等)。比較は定数時間比較で行う。
  • 署名検証には生のリクエストボディを使う(JSONパース後に再シリアライズすると一致しない)。
  • タイムスタンプを検証し、古いリクエストを拒否してリプレイ攻撃を防ぐ。
  • 可能なら送信元IPの許可リストも併用する。
  • エンドポイントURLは推測されにくくしてもよいが、それだけを防御にしない。

5.3 冪等性と順序

  • Webhookは重複配信される前提。イベントIDで処理済み判定をする。
  • 順序は保証されない前提。updated が created より先に届くこともある。
    • イベントのタイムスタンプやバージョンで古いイベントを破棄する。
    • または、Webhookを「変更通知」として扱い、最新状態をAPIで取得し直す(Thin Event方式)。
  • ペイロードの内容を鵜呑みにせず、重要な処理(決済完了など)はAPIで状態を再確認する。

5.4 取りこぼし対策

  • Webhookは届かないことがある。定期的な突合バッチ(Reconciliation)で補完する。
  • 受信エンドポイントの障害時に備え、SaaS側の再送ポリシー(回数・期間)を把握しておく。
  • 管理画面からの手動再送手順を Runbook に記載する。

6. データ同期

6.1 同期方式

方式特徴
Webhook(Push)リアルタイム性が高い。取りこぼし対策が必要
ポーリング(Pull)実装が単純。差分取得APIがないと非効率
差分同期(updated_since / カーソル)効率的。最終同期位置の永続化が必要
全件同期確実だが重い。定期的な整合性チェック用に
ハイブリッドWebhookで即時反映+定期バッチで突合(推奨)

6.2 設計ポイント

  • 項目ごとにマスター(Source of Truth)を決める。双方向同期は競合解決ルールを必ず定義する。
  • 同期のチェックポイント(最終カーソル、最終更新日時)は処理成功後に更新する。
  • 差分取得の境界は少しオーバーラップさせ(例:前回時刻の数分前から)、取りこぼしを防ぐ。冪等処理で重複を吸収する。
  • 削除の同期を忘れない(論理削除フラグ、削除イベント、全件突合)。
  • 時刻はUTCで扱い、タイムゾーンの混在を避ける。

6.3 外部データのキャッシュ

  • 頻繁に参照する外部データはキャッシュし、外部障害時もある程度動けるようにする。
  • キャッシュの鮮度要件と無効化タイミングを明確にする。

7. エラーハンドリングと障害対応

7.1 エラー分類

分類例対応
一時的エラータイムアウト、503、429リトライ
恒久的エラー400、422(バリデーション)リトライせずエラー記録・通知
認証エラー401、トークン失効トークン更新 → ダメなら再連携を促す
権限エラー403設定ミスの可能性。アラート
想定外未知のレスポンス形式安全側に倒して失敗扱い・アラート

7.2 デッドレターキュー(DLQ)

  • リトライ上限を超えたジョブはDLQに退避し、失われないようにする。
  • DLQの件数を監視し、原因修正後に再処理できる仕組みを用意する。

7.3 フォールバック

  • 外部障害時の挙動を機能ごとに決めておく:
    • 処理を保留してキューに溜め、復旧後に再送
    • キャッシュ値で応答
    • 機能を一時的に無効化し、ユーザーに案内表示
    • 代替ベンダーへの切り替え(重要機能のみ)

7.4 不整合への備え

  • 「外部では成功したが自社側の記録に失敗した」ケースを想定し、突合と補正の手順を用意する。
  • 重要な処理(課金など)は、状態遷移を明示的に管理する(pending → confirmed / failed)。

8. セキュリティとコンプライアンス

  • 送信データを最小化する。 SaaSに渡す個人情報は必要な項目だけに絞る。
  • 個人情報を送る場合は、プライバシーポリシー・委託先管理・越境移転(データ保管リージョン)を確認する。
  • SaaSのセキュリティ認証(SOC 2、ISO 27001、ISMAP 等)を確認する。
  • ログにトークン・個人情報・カード情報を出力しない(マスキング)。
  • 外部から受け取ったデータは信頼せず、バリデーション・サニタイズする(SSRF、インジェクション対策)。
  • ユーザーの退会・削除要求時に、外部SaaS側のデータも削除する手順を用意する。
  • SaaS側の管理者アカウントはSSO・MFAを必須にし、担当者の異動時に権限を見直す。
  • 依存SDKの脆弱性をスキャン対象に含める。

9. 可観測性(ログ・メトリクス・トレース)

9.1 ログ

  • 外部呼び出しごとに記録する:連携先、エンドポイント、メソッド、ステータスコード、レイテンシ、リトライ回数、相関ID。
  • SaaSが返すリクエストID(X-Request-Id 等)を保存する(ベンダー問い合わせ時に必須)。
  • リクエスト/レスポンス本文は、マスキングした上で必要な範囲に限る。

9.2 メトリクスとアラート

メトリクスアラート例
エラー率(連携先別)5分間で5%超
レイテンシ(p95/p99)通常の2倍超
レート制限残量残り10%未満
キュー滞留数・DLQ件数閾値超過
Webhook受信数通常時間帯に一定時間ゼロ(取りこぼし検知)
トークン・証明書の有効期限期限30日前
サーキットブレーカーの状態オープン時

9.3 トレース

  • 分散トレーシングで外部呼び出しをスパンとして記録し、遅延の原因を特定できるようにする。

10. テスト戦略

レベル内容
ユニットテストアダプタ層をモックし、業務ロジックを検証
アダプタのテスト記録済みレスポンス(VCR等)やスタブサーバーでマッピングを検証
契約テスト外部APIのレスポンス形式が想定通りか定期的に確認
サンドボックス結合テストSaaSのテスト環境で実際のフローを通す
障害注入テストタイムアウト・5xx・429・不正レスポンス・Webhook重複/順序逆転を再現
本番スモークテストデプロイ後に低リスクな読み取りAPIで疎通確認
  • サンドボックスと本番の挙動差(レート制限、機能差、データ)を把握しておく。
  • テストで本番キーを使わない。CIに本番シークレットを置かない。

11. バージョン管理と変更追従

  • APIバージョンを明示的に固定する(ヘッダーやURLで指定)。デフォルト=最新に依存しない。
  • SaaSのチェンジログ・廃止予定(Deprecation)通知・ステータスページを購読し、担当者を決める。
  • レスポンスの未知フィールドは無視し、必須フィールドの欠落は検知できるようにする(寛容な受信)。
  • 列挙値(ステータス等)に未知の値が来ても落ちないようにする。
  • バージョンアップは、ステージングで検証 → 段階的に本番切り替え(フィーチャーフラグ活用)。

12. ベンダー選定とロックイン対策

12.1 選定時の確認項目

  • API の品質(ドキュメント、SDK、サンドボックス、Webhook、冪等キー、バルクAPI)
  • SLA と過去の障害実績、ステータスページの有無
  • レート制限と料金体系(従量課金の上限、超過時の挙動)
  • データエクスポート手段(解約時に全データを取り出せるか)
  • セキュリティ認証、データ保管リージョン、サポート体制
  • ベンダーの事業継続性

12.2 ロックイン対策

  • 腐敗防止層で抽象化し、差し替えコストを下げる。
  • 重要なデータは自社DBにも保持する(外部にしかないデータを作らない)。
  • 出口戦略(移行手順、データ移行方法)を事前に検討しておく。
  • コスト監視:API呼び出し数・従量課金額をダッシュボード化し、異常な増加を検知する。

13. 運用

  • 連携台帳を整備する:連携先、用途、担当者、契約、認証方式、シークレットの保管場所、送受信データ、SLA、サポート窓口。
  • Runbook を用意する:
    • 連携先障害時の対応(フォールバック切り替え、ユーザー告知)
    • シークレット漏洩時の失効・再発行
    • DLQ・未処理データの再処理
    • Webhookの手動再送・突合
  • SaaSのステータスページを監視ツールに取り込む。
  • 定期棚卸し:使われていない連携、不要な権限、期限切れ間近の証明書・トークンを確認する。
  • SaaS側の管理画面での設定変更も変更管理の対象にする(誰がいつ何を変えたか)。

14. チェックリスト

設計

  • アダプタ層で外部APIを抽象化している
  • 項目ごとの Source of Truth を定義した
  • 同期/非同期の使い分けを決めた
  • 外部障害時のフォールバック挙動を決めた
  • IDマッピングの方法を決めた

実装

  • すべての外部呼び出しにタイムアウトがある
  • 指数バックオフ+ジッターでリトライし、対象エラーを限定している
  • 書き込み操作に冪等キーを使っている
  • 自社側でレート制限を制御している
  • サーキットブレーカー/バルクヘッドを導入した
  • APIバージョンを固定している

Webhook

  • 署名とタイムスタンプを検証している
  • 即座に2xxを返し、非同期で処理している
  • イベントIDで重複排除している
  • 順序逆転に対応している
  • 定期突合で取りこぼしを補完している

セキュリティ

  • シークレットを専用ストアで管理し、環境ごとに分離している
  • ローテーション・失効手順がある
  • 要求スコープ・権限が最小限
  • ログに機密情報が出ない
  • 送信する個人情報を最小化し、法務確認済み

運用

  • 連携先別のエラー率・レイテンシ・キュー滞留を監視している
  • トークン・証明書の期限を監視している
  • DLQと再処理の仕組みがある
  • Runbook と連携台帳がある
  • チェンジログ・廃止予定の確認担当がいる

0件のページ