銀行エンジニアリング 01:残高ではなく「記録」を正とする
10万円の振込を1件だけ取り上げ、画面の残高がどんな記録から計算されているのかを、実際のMiniBankの仕訳で追いかけます。
v8以降に実装した台帳・記帳エンジンなどを実際に動かし、正常時と異常時の両方から銀行システムの構造を学ぶ。
Prerequisite: BANKING SYSTEMLEARNING GOALS
このページで学ぶこと
- 残高と台帳(Ledger)の違い
- 仕訳(Journal)と借方・貸方
- 10万円の振込がMiniBankの中でどう記録されるか
PLAIN LANGUAGE
まず、ここだけつかむ
残高は記録を足し引きした結果で、銀行が守っているのは記録(仕訳)そのものです。
顧客の預金は銀行にとって負債なので、貸方で増え、借方で減ります。
同じ銀行内の10万円振込は、Aさんの預金を借方、Bさんの預金を貸方に記録する1件の仕訳です。
DIFFICULT PARTS, STEP BY STEP
難しいポイントを、順番にほどく
専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。
Ledger(台帳)
お金の動きの記録の集まりです。
残高の理由を説明できるようにするためです。
ledger.journal_entries と journal_lines。追記専用です。
借方・貸方
仕訳を記録する左右の欄です。
1つの出来事の両面を必ず記録し、合計を一致させるためです。
各明細は借方か貸方の片方だけが正の金額です。
cached_balance
仕訳から計算した残高を保存した値です。
毎回すべての仕訳を集計すると表示が遅くなるためです。
記帳のたびに仕訳から計算し直され、ずれは照合で検出します。
Level 1 基礎:残高は「結果」、台帳は「理由」
通帳に表示される残高は、それまでのすべての入出金を足し引きした「結果」です。銀行が本当に守っているのは結果の数字ではなく、1件1件の「いつ・なぜ・いくら動いたか」という記録です。この記録の集まりを台帳(Ledger)と呼びます。
記録が正しければ残高はいつでも計算し直せます。逆に残高だけを上書きする仕組みでは、数字が合わなくなったときに原因を説明できません。
| 言葉 | 一言で | MiniBankでの名前 |
|---|---|---|
| 台帳(Ledger) | 記録の集まり | ledger.journal_entries / ledger.journal_lines |
| 仕訳(Journal Entry) | 1件の出来事の記録 | journal_entries の1行 |
| 仕訳明細(Journal Line) | どの勘定にいくら借方・貸方か | journal_lines の行(片側だけ正の金額) |
| 勘定(Ledger Account) | 金額を集計する箱 | ledger.ledger_accounts(顧客ごとの預金勘定もここ) |
| 残高キャッシュ | 表示を速くするための計算結果 | public.bank_accounts.cached_balance |
Level 1 基礎:借方(Debit)と貸方(Credit)
複式記帳では、1つの出来事を必ず2つ以上の勘定に記録し、借方の合計と貸方の合計を一致させます。借方・貸方は「増える・減る」ではなく、記録する欄の左右です。
銀行にとって顧客の預金は「いつか返さなければならないお金」、つまり負債(Liability)です。負債は貸方に記録すると増え、借方に記録すると減ります。
| 勘定の種類 | 増えるとき | 減るとき | MiniBankの例 |
|---|---|---|---|
| 資産(Asset) | 借方 | 貸方 | SYSTEM_FUNDING(受け入れた現金の見立て) |
| 負債(Liability) | 貸方 | 借方 | 顧客ごとの預金勘定(DEP-口座番号) |
| 収益(Revenue) | 貸方 | 借方 | FEE_REVENUE |
| 費用(Expense) | 借方 | 貸方 | INTEREST_EXPENSE |
Level 2 銀行業務:10万円を振り込むと何が起きるか
Aさんが同じMiniBankのBさんへ10万円を振り込むと、銀行の中では「Aさんに返す義務が10万円減り、Bさんに返す義務が10万円増える」という1件の出来事になります。銀行の現金は動きません。
- 01顧客操作(振込依頼)
- 02本人確認(ワンタイムコード)
- 03振込の業務記録(public.transfers)
- 04会計ルール TRANSFER_STANDARD
- 05仕訳(借方:Aさんの預金 / 貸方:Bさんの預金)
- 06各勘定の残高
- 07残高キャッシュの再計算
| 明細 | 勘定 | 借方 | 貸方 | 意味 |
|---|---|---|---|---|
| 1 | Aさんの預金勘定(負債) | 100,000 | — | Aさんへ返す義務が減る |
| 2 | Bさんの預金勘定(負債) | — | 100,000 | Bさんへ返す義務が増える |
Level 3 システム:MiniBankでの実装(正常時)
仕訳はすべて ledger.post_journal_entry() という1つの入口から書き込まれます。この関数は、関係する顧客口座をロックし、借方合計と貸方合計が一致しない仕訳を拒否し、最後に仕訳明細から cached_balance を計算し直します。
仕訳とその明細は追記専用です。UPDATE と DELETE はトリガーで拒否されるため、「残高を直接直す」操作はそもそも存在しません。
-- 顧客預金勘定(負債)の残高 = 貸方合計 − 借方合計
select sum(credit_amount) - sum(debit_amount)
from ledger.journal_lines
where ledger_account_id = :deposit_account;異常時:残高キャッシュと台帳が食い違ったら
| 段階 | 内容 |
|---|---|
| 症状 | 画面の残高と、仕訳から計算した残高が一致しない |
| 調査 | 管理画面 Ledger・Posting の「残高再計算で差異がある口座」で、口座ごとに仕訳残高・cached残高・差額を確認する |
| 考えられる原因 | キャッシュの更新漏れ、手作業によるDB直接更新、仕訳側の欠落 |
| 真因の決め方 | 仕訳は追記専用で借方=貸方が保証されているため、まずキャッシュ側を疑う。仕訳全体の借方=貸方が崩れていれば台帳側の問題 |
| 対応 | v7から、差異がある口座への新しい記帳は LG018 で止まる。修正は、提案者と別の担当者が確認する残高修復手続き(propose_balance_repair → confirm_balance_repair)で行う |
| 再発防止 | キャッシュは台帳から再計算するだけの派生値として扱い、直接更新する経路を作らない |
Level 4 アーキテクチャ:System of Record はどちらか
MiniBankでは台帳(仕訳)が System of Record(最終的に正しい記録)で、cached_balance は読み取り性能のための派生データです。両者が食い違ったときにどちらを信じるかを設計時に決めておくことが、障害時の判断を速くします。
派生データを持つ以上、ずれは起こり得るものとして扱い、検出(照合)と止める仕組み(LG018)を最初から組み込んでいます。
どこを確認すればよいか
- 管理画面「会計・Ledger」→ 仕訳帳:1件の仕訳の借方・貸方と適用ルール
- 管理画面「Ledger・Posting」:残高再計算の差異一覧
- 管理画面「会計・Ledger」→ 試算表:全体の借方合計=貸方合計
CHECK YOUR UNDERSTANDING
3問で理解チェック
答えを頭の中で考えてから、各項目を開いて確認してください。
Q1顧客が預金すると、銀行の負債は増えますか?減りますか?
増えます。顧客へ返す義務が増えるからです。
Q2同じ銀行内の振込で、銀行の現金は動きますか?
動きません。返す相手が変わるだけです。
Q3cached_balance と仕訳が食い違ったら、どちらを正としますか?
仕訳です。cached_balance は派生値です。
完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。
SOURCE TRAIL
根拠と関連教材
CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。
確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。
Implementation Status: IMPLEMENTED IN MINIBANK(この教材が扱うコードの範囲)
- RELATED LESSON/docs/ledger台帳と複式記帳の基本
- RELATED LESSON/docs/transfer振込を1件ずつ追う
- HISTORICAL IMPLEMENTATION
supabase/migrations/20260819155223_create_post_journal_entry_function.sqlv7までの仕訳の書き込み口(現在も v8 から利用) - CURRENT IMPLEMENTATION
supabase/migrations/20260924100000_v8_posting_engine.sqlposting.recompute_balances / posting.run_integrity残高再計算と整合性検査