課題とスコープ
注文システムが1日あたり2億件のトランザクションを生成します。内部元帳は注文、支払い、返金、手数料を記録し、決済プロバイダーは決済バッチレポートを提供し、銀行は実際の入金を提供します。前日分を翌朝06:00までに完了し、欠落、重複、金額、手数料、通貨の差分を特定し、再実行、人手によるレビュー、返金、複数通貨、監査証跡をサポートするT+1パイプラインを設計してください。
内部元帳をビジネス上の信頼できる唯一の情報源(Source of Truth)とし、プロバイダーと銀行を外部の信頼できる情報源と見なします。合計値が偶然一致したからといって、照合によって元帳を上書きしたり、行レベルの差分を隠蔽したりしてはなりません。金額、通貨、決済日、バッチ、証跡は追跡可能である必要があります。
面接官がテストしているポイント
第1に、ツールを選択する前に、ファクト、時間、不変条件を定義できるかです。トランザクション日、転記日、決済日、銀行起算日(バリューデート)を区別します。金額は最小通貨単位で保存し、通貨を必須とし、繰り返しの取り込みを安全に行えるようにします。
第2に、突合(マッチング)を全テーブル結合ではなく階層化された戦略にできるかです。プロバイダーのトランザクションIDとバッチIDから始め、次に注文ID、金額、通貨、時間枠の制約付きの組み合わせを使用します。曖昧な一致(ファジーマッチ)は、自動で確定させるのではなく、必ずレビューに回す必要があります。
第3に、不一致の解決ループを閉じられるかです。すべての不一致には、タイプ、深刻度、証跡、担当者、ステータス、期限、修正アクションが必要です。修正によって、元の不一致を保持したまま新しい照合バージョンを作成します。
回答前に明確にすべき質問
- スコープに含まれるものは何か? オーソリ(承認)、売上確定(キャプチャ)、返金、手数料、出金(ペイアウト)、銀行入金、またはそのすべてか?
- 06:00を定義するタイムゾーンはどれか? 決済やレポートの生成がサマータイムの変更や祝日をまたぐことはあるか?
- 外部レポートは増分で取得できるか? ファイル名、カーソル、バージョン、再試行可能なダウンロードは安定しているか?
- パイプラインが元帳を自動調整することは許可されているか? 承認されたワークフローが承認する調整案を作成すると想定します。
- どの為替(FX)ポリシーが適用されるか? 取引通貨、決済通貨、銀行通貨を保持し、レートソースと精度を定義します。
- 解決のSLAは何か? 高額案件、コンプライアンス関連、顧客に影響を与える不一致についてエスカレーションを定義します。
30秒で答えるフレームワーク
「まず3つのデータソースをすべて不変のRawレイヤーに保持し、金額、通貨、時間、外部IDを正規化します。取り込みはレポートバージョンによってべき等(Idempotent)にします。強力なキーで最初に一致させ、次に制約付きの複合キーで一致させ、不確実な結果はレビューキューに入れます。各バッチには行レベル、グループ別、合計の統制を設けます。不一致は監査可能なステートマシンを使用し、承認された調整仕訳によって修正されます。期限から逆算してスケジュールを設定し、ファイルの完全性、レイテンシ、一致率、未解消金額、再実行を監視し、重複ファイル、遅延返金、部分決済、為替変動に対するテストを実施します。」
ステップごとの詳細解説
ステップ1: ファクトモデルと会計ウィンドウの定義
不変の internal_entry、provider_entry、bank_entry ファクトを作成します。ソースファイルのハッシュ、行番号、ソース、取り込み時間、レポートバージョン、業務日付、決済日を保持します。金額は浮動小数点数ではなく整数の最小単位として保存し、通貨を必須とします。注文ステータスと資金ステータスを分離し、「支払い成功」が「着金完了」として扱われないようにします。
ステップ2: ソースバージョンごとにべき等に取り込む
ダウンロードしたデータはまずオブジェクトストレージに書き込み、その後サイズ、ハッシュ、署名、期待される行数を検証します。一意のキーとして source + report_id + version + row_number を使用します。重複してダウンロードされた場合は取り込みレコードが追加されますが、金額が2重に転記されることはありません。ファイルを上書きするのではなく、プロバイダーの古いバージョンを保持し、差し替え関係を記録します。
unique_key = source + report_id + version + row_number
if unique_key already exists: record_duplicate_download()
else: persist_raw_row()ステップ3: 再実行可能な正規化レイヤーの構築
バージョン管理されたディクショナリを通じて、ステータス、金額の符号、タイムゾーン、手数料タイプ、外部IDをマッピングします。各変換は normalization_run_id を書き込みます。固定された入力スナップショットとルールバージョンにより、再実行が決定論的になります。未知のステータス、マイナス金額のセマンティクス、通貨は、デフォルトでゼロにするのではなく検疫(クランティン)に送られます。
ステップ4: 階層化されたマッチングの使用
レイヤー1では、安定したプロバイダーのトランザクションID、返金ID、出金IDを使用します。レイヤー2では、同一の加盟店、通貨、決済日の範囲内で、注文ID、金額、許可された時間枠を使用します。レイヤー3では候補のみを生成します。異なる通貨での同額、複数の外部行に分割された1つの内部項目、複数の決済に分割された1つの外部行は、すべてレビューが必要です。
ステップ5: 3つの統制による結果の検証
行統制は、各ファクトが最大で1回しか一致していないことを検証します。グループ統制は、バッチ、通貨、決済日、手数料タイプ別に行数と金額を比較します。合計統制は、元帳、プロバイダーレポート、銀行入金の間で期首残高、入金、出金、手数料、返金、期末残高を比較します。不一致がある場合は各レイヤーの証拠を保持し、単一の「合格」フラグだけで済ませてはなりません。
ステップ6: 不一致をステートマシンにする
最低限、欠落、重複、金額の不一致、手数料の不一致、通貨または為替の不一致、日付のズレ、未知の外部ステータスに分類します。状態としては open、investigating、adjustment_pending、resolved、accepted があり、すべての状態遷移に実行者、理由、証拠、タイムスタンプを付与します。高額な不一致やコンプライアンス上の不一致には2名による承認が必要です。自動化は提案の作成のみを行います。
ステップ7: 遅延データ、返金、部分決済の処理
レポートが遅延した場合、受信した範囲のみをクローズし、バッチは未完了のままにします。一時的な未着を恒久的な欠落レコードにしてはなりません。返金やチャージバックは取引日より後に発生する可能性があるため、イベント日と決済日を別々に記録します。1つのトランザクションが複数の出金に分割される場合は、1対多の関係を保持します。残高が一致していても未照合の行が消えるわけではありません。
ステップ8: リカバリ、再実行、監査の計画
すべての実行について、入力ファイルのマニフェスト、スナップショット時刻、ルールバージョン、マッチング結果、出力された不一致を保存します。最後に成功したステージから再開し、実行IDによって再実行出力を分離し、一意のキーによってマージします。バッチ、トランザクション、または不一致ごとに追跡できるように、Rawファイル、ハッシュ、レポートバージョン、人間の判断、調整伝票を保持します。Stripeも残高、トランザクション、出金を分離し、出金IDを使用してその残高トランザクションを取得することを推奨しており、外部の決済バッチを1つの合計値に還元できない理由を示しています。
トレードオフと境界
トレードオフ1: 強力なキーか、より高い一致率か
強力なキーによる自動化は一致する行数が少なくなる可能性がありますが、誤検知のコストは抑えられます。金額や時間枠を広げるとカバー率は向上しますが、類似したトランザクションを誤って統合するリスクが高まります。しきい値、ウィンドウ、フォールバックをバージョン管理し、非決定論的な結果はすべてレビューに送ります。
トレードオフ2: 日次バッチか、ニアリアルタイムか
T+1のバッチは再現や監査が容易です。ニアリアルタイムのストリームは影響の大きい問題を早期に顕在化させますが、レポートのバージョン、遅延データ、重複イベントを処理する必要があります。バッチを会計上の確定結果とし、ストリームをアラートレイヤーとして使用します。
トレードオフ3: 自動調整か、人手による承認か
軽微で明確かつ元に戻すことが可能な差異には、制約付きの自動化を使用できます。高額、通貨関連、二重請求、コンプライアンスに関する差異には人手による承認が必要です。どちらのパスも履歴を直接編集するのではなく、元のファクトと調整伝票を保持します。
障害訓練と進化計画
訓練1: 重複ファイルと重複行
同じレポートを3回配信し、ファイルハッシュ、レポートバージョン、行キーによって二重転記が防止されることを検証します。1つのトランザクションを2つのバージョンに含め、バージョンの関係性と選択ルールが説明可能であることを確認します。
訓練2: 遅延返金と部分決済
決済の2日後に返金を配信し、1つの注文を2つの出金に分割します。取引日、決済日、入金日が別々に維持され、新しい実行によって過去の実行を変更することなく不一致が解消されることを検証します。
訓練3: ページの欠落と誤った為替レート
レポートから1ページを削除するか、レートテーブルを差し替えます。行数、ハッシュ、合計、通貨の統制によって公開がブロックされ、照合完了と宣言されるのではなく、バッチが完了待ちとしてマークされる必要があります。
よくある間違いとフォローアップ
間違い1: 3つの合計値のみを比較する
合計が一致していても、1件の欠落と1件の重複が隠れている可能性があります。行の一致、グループ別統制、合計統制を維持してください。
間違い2: お金を浮動小数点数で保存する
浮動小数点誤差により誤った差分が発生します。整数の最小単位または固定小数点を使用し、常に通貨を保存してください。
間違い3: 古いレポートの上書き
上書きすると監査や再実行の証拠が失われます。レポートは不変として保存し、修正はバージョンとして表現してください。
間違い4: 曖昧な一致(ファジーマッチ)の自動転記
金額が同じで時間が近いからといって、同一である証明にはなりません。曖昧な結果は候補の証拠とともにレビューに回してください。
間違い5: 支払いの成功を銀行入金として扱う
支払いステータス、プロバイダーの決済、銀行入金は異なるファクトです。これらを個別にモデル化し、バッチの関係性を通じて接続してください。
間違い6: レポート生成とタイムゾーンの無視
レポートは決済後に入手可能になる場合があります。タイムゾーンや祝日によって誤った欠落レコードが作成されることがあります。ソースの提供保証時間から逆算してスケジュールを設定し、タイムゾーンを明示的に保存してください。
間違い7: 問題を修正するために過去の元帳行を編集する
直接編集すると元の証拠が失われます。不一致と新しい実行に紐づけられた、承認済みの調整仕訳を使用してください。