
Summary
この文書の要点
- Webhook受信では署名検証とイベント保存を優先する。
- 外部イベントIDで重複を防ぐ。
- Webhook受信は短く終え、重い処理はキューへ渡して再試行できるようにする。
- message_idやevent_idで冪等性を確保し、同じ通知を二重処理しない。
どこが設計の難所か
Webhook内でDB更新、通知、返信、ファイル取得まで同期実行すると、外部サービスのタイムアウトに間に合わないことがあります。失敗時に再送されると、同じ通知や登録が二重に実行される危険があります。
外部サービスのWebhookは、署名、リトライ、イベント形式、応答時間の制約を持ちます。アプリ側では内部ユーザーと外部ユーザーIDの紐づけ、ブロックや連携解除も扱う必要があります。
メッセージ連携では、受信したイベントへすぐ返信したくなります。しかし外部サービスからの再送やタイムアウト、アプリ側の処理遅延があると、同期処理は脆くなります。まず受け取った事実を保存することが重要です。
境界をどう切るか
受信エンドポイントでは、署名検証、イベントID保存、最小限の正規化だけを行い、業務処理はキューへ渡します。内部処理では、イベント種別ごとにハンドラを分け、冪等性を保ちます。
設計では、Webhook入口、イベント保存、業務処理、返信送信を分けます。入口では署名検証と重複チェックだけを行い、処理キューへ渡します。業務処理は再実行しても同じ結果になるようにします。
実装で効く細部
DBには外部イベント、外部ユーザー紐づけ、処理状態、最終エラーを保存します。返信が必要な処理は、送信ログを持ち、同じイベントに対して同じ返信を二重送信しないようにします。
HonoやLaravel側では、eventsテーブルにevent_id、source_id、type、payload_hash、received_at、statusを保存します。返信や外部API呼び出しはoutboxテーブルに積み、送信成功、失敗、再試行回数を管理します。
- 署名検証に失敗したpayloadは処理せず、必要な範囲で監査ログだけ残す。
- 同じevent_idの再送は既存処理結果を見て成功応答にできるようにする。
- 返信送信はoutbox patternで管理し、業務更新と送信要求を同じトランザクションに入れる。
壊れ方を観測する
検証では、署名不正、同一イベント再送、処理途中失敗、外部ユーザー未紐づけ、ブロック済みユーザーのケースを確認します。Webhook受信のレスポンス時間も測ります。
検証では、同一イベント再送、順序逆転、返信API失敗、キュー再試行、署名不一致を確認します。ローカルでは保存済みpayloadを再投入できるCLIを用意すると、障害調査が容易になります。
捨てた選択肢とトレードオフ
キューに分けると処理完了まで遅延が出ます。即時返信が必要な操作では、受信時に返すメッセージを限定し、重い処理は後続通知に回すなどUXとの調整が必要です。
同期返信は実装が短く体験も分かりやすい一方、外部APIの遅延に巻き込まれます。非同期化すると状態管理は増えますが、再試行と監査がしやすくなります。重要な業務処理ほど非同期境界を置く価値があります。
現場に残す判断軸
Webhook連携では、受け取ることと処理することを分けるだけで堅牢性が上がります。署名検証、イベント保存、冪等処理を基本にすれば、外部サービスの再送にも対応しやすくなります。
Webhook連携では、速く返すことと確実に処理することを分けます。受信記録とoutboxを持つだけで、再送に強い連携へ変わります。


