はじめに
ポイント監査ログの心臓部は、「1つの出来事をどう1行のデータにするか」です。ここの設計が甘いと、後から「なぜこの残高になったの?」に答えられません。
この記事では、POS(スマレジ)とECをまたいでポイントを運用したプロジェクトで、私が実際に設計したイベント記録のスキーマを解説します。付与・消費・変換という3つの操作を、どれも同じ形で残す。そのための考え方を、なるべく具体的にお伝えします。
イベントを記録するという発想
残高を「上書き」せず「積み上げる」
多くのシステムは、顧客ごとに「残高:1000」という数字を1つ持ち、増減のたびに上書きします。シンプルですが、上書きは過去を消してしまうのが弱点です。
私が採った設計は逆で、残高そのものは持たず、増減の記録だけを1行ずつ積み上げます。「+100」「-500」「+300」というイベントの合計が、その時点の残高になる。銀行の通帳と同じで、行を消さない限り履歴は永遠に追えます。この積み上げ方式こそが、監査可能性の出発点なんです。
3つの操作を同じ形で残す
ポイントの動きは、突き詰めると3種類です。付与(増える)、消費(減る)、変換(クーポンなどに引き換える)。これらを別々のテーブルで管理すると、突合のたびに3つを見比べる羽目になります。
そこで、3つとも「操作種別」という項目で区別する1つのログにまとめました。同じ形なら、合計するだけで残高が出ますし、一覧で眺めたときも流れが読めます。
1イベントに持たせる項目
最低限そろえたい5W1H
イベントの1行は、後から見て「誰が読んでも意味が分かる」ことが理想です。私は5W1Hを埋めるつもりで項目を決めました。
特に balance_after(操作後の残高)は省略されがちですが、これがあると「この行の直後にいくつだったか」が一目で分かり、障害時の突合がぐっと楽になります。
付与ソースを必ず残す
source(付与ソース)は、不正やミスを見抜くための重要な項目です。同じ「+100」でも、購入によるものと手動付与では意味がまったく違います。
手動付与だけを後から抽出できれば、「誰がどれだけ手で付けたか」が見えます。金銭を発行する操作なのですから、その出所を必ず記録に残す。これはセキュリティというより、あって当たり前の会計的な作法だと考えています。
参照キーで元の取引とつなぐ
イベント単体だと「なぜ付いたのか」までは分かりません。そこで、元になった注文番号やキャンペーンIDを reference として持たせます。
この参照キーは、後述する二重付与の防止にも使えます。「この注文に対する付与イベント」を一意に決められるので、同じ注文で二度付与しそうになったときに気づけるんです。
冪等性で「二重記録」を防ぐ
なぜ同じイベントが2回来るのか
システム連携では、ネットワークのタイムアウトや再送で「同じ操作の指示が2回届く」ことが日常的に起こります。素直に2回記録すると、ポイントも2回付いてしまいます。
「注文123の付与」のように操作を一意に表すキーを作る
同じキーのイベントが既にあれば、それは再送とみなす
重複なら黙って無視し、残高は動かさない
キー設計が冪等性のカギ
冪等性を効かせられるかは、この一意キーの設計にかかっています。「顧客ID+注文番号+操作種別」のように、業務的に重複しない組み合わせで作るのがコツです。
キーは業務の意味から作る
ランダムなIDではなく、「その操作が現実に1回しか起きないはずの単位」からキーを組み立てます。こうすると、送り手が別でも、同じ現実の出来事なら同じキーになり、自然と重複を弾けます。
このキーをデータベース側で「重複禁止」に設定しておけば、万一チェックをすり抜けても、登録の段階で二重記録を止められます。二段構えにしておくと安心です。
まとめ
イベント記録の設計で私が大事にしたのは、次の3点です。
- 残高を上書きせず、増減を積み上げる — 履歴が消えないので後から追える
- 付与・消費・変換を同じ形で残す — 合計するだけで残高が出る
- 一意キーで冪等性を担保する — 再送が来ても二重記録しない
こうして残したイベントは、障害時の強い味方になります。ズレた残高をログから再構築する具体的な手順は「障害時の突合・復旧に使えるログの持ち方」で解説します。全体像を振り返りたい方は、ハブ記事「ポイント監査ログ」もあわせてどうぞ。