業務をデータモデルへ翻訳する
画面の項目をそのままtableにするのではなく、現実の業務・契約・状態・証拠を、壊れにくいデータ構造へ翻訳する方法を学びます。
境界、transaction、並行制御、脅威、テスト、障害対応をFailure Firstで学ぶ。
先に学ぶ内容 — Prerequisite: 基礎 / 銀行システム学ぶこと — LEARNING GOALS
このページで学ぶこと
- Entity・Aggregate・Eventの違い
- 状態機械と不変条件の作り方
- 正規化・snapshot・派生値の使い分け
体験28 · DOMAIN MODELING(記録の役割を分ける)
定期預金を100件契約したら、会計上の勘定も100個に増えるか確かめよう
まずは予想をひと言。本文を読んだ後、教材内の参照元で実際のテーブル定義を確認します。
- 01まず予想する — PREDICT
まずここだけ:思ったことをひと言書く
定期預金を100件契約したら、会計上の『定期預金負債』という勘定も100個に増えると思いますか?
正解でなくて大丈夫です。「〜だと思う」から始めてみましょう。
ヒント1を見る
契約ごとの記録と、会計全体でまとめる記録は同じ粒度でしょうか?
ヒント2を見る
100件の契約を1つの勘定科目で表せると、何が便利でしょうか?
もっと詳しく:この問いで使う専門用語
- Aggregateは、同じtransactionで不変条件を守る業務上のまとまりです。
- Snapshotは、取引が起きた時点の値を固定した記録です。
- Projectionは、正式記録から作る表示・検索用の派生値です。
日常語で理解する — PLAIN LANGUAGE
まず、ここだけつかむ
データモデルは画面項目の保存場所ではなく、現実の契約・状態・証拠を表すmodelです。
現在のmaster、取引時点のsnapshot、消せないevent、表示用cacheは目的が違います。
状態機械は状態名の一覧ではなく、許可する矢印と競合時の結果まで定義します。
順番に理解する — DIFFICULT PARTS, STEP BY STEP
難しいポイントを、順番にほどく
専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。
Aggregate
同じtransactionで不変条件を守る業務上のまとまりです。
どこまでを一緒にlock・commitするか決める基礎になります。
定期預金契約と作成時の会計記録は、同じ業務成功として結びます。
Snapshot
取引が起きた時点の値を固定した記録です。
後で顧客名やruleが変わっても当時の説明を再現できます。
振込時点の銀行・支店・口座名義・手数料を業務recordへ残します。
Projection
正式記録から作る表示・検索用の派生値です。
速く読めますが、壊れた時に再構築できる必要があります。
cached_balanceはLedgerと照合可能なprojectionです。
発展 — ADVANCEDもっと詳しく確認する最初の学習では読まなくても問題ありません。仕組みをもっと深く理解したい場合に開いてください。
最初に『名詞』と『出来事』を分ける
| 種類 | 問い | Minibankの例 |
|---|---|---|
| Entity | 同一性を保ちながら変化するものは何か | customer、bank_account、time_deposit |
| Value | 値そのものに意味があるものは何か | 金額、通貨、銀行・支店情報 |
| Aggregate | 同じ整合性境界で守るまとまりは何か | 振込依頼とOTP challenge、口座とledger account |
| Event | 何が起きた事実を残すか | TRANSFER_COMPLETED、KYC_APPROVED |
| Projection | 表示を速くする派生値は何か | cached_balance、管理dashboard集計 |
状態機械は、許可する矢印を設計する
- 01業務で存在する全状態を列挙
- 02開始状態と終了状態を区別
- 03操作ごとに許可される遷移を定義
- 04競合時にどちらが勝つか決定
- 05終了状態の再実行・取消方針を定義
- 06DB制約・lock・testへ落とす
正規化・snapshot・派生値
| 方法 | 向いているもの | 注意点 |
|---|---|---|
| 正規化 | 現在の顧客・口座・operatorの一貫した属性 | 過去時点の表示を後から再現できない場合がある |
| Snapshot | 取引時点の振込先名義・銀行・手数料rule | 元データとの重複を意識的に管理する |
| Append-only event | 金額変動や重要操作の履歴 | 訂正方法と検索・集計を別途設計する |
| Derived cache | 残高やdashboardの高速表示 | 正式記録と照合・再計算できなければならない |
設計演習:定期預金を分解する
- 契約identity、顧客、引落口座、元本、期間、金利、満期日を分類する
- active・cancelled・maturedの遷移と禁止遷移を書く
- 普通預金負債と定期預金負債の仕訳を対応づける
- 契約別Subledger合計とGL統制勘定の一致条件を書く
- 作成requestの再送と、満期・解約の競合を考える
実際のMinibankでは
`public.time_deposits`は契約別Subledger、`TIME_DEPOSITS_PAYABLE`はpooled liability accountです。契約状態と仕訳は別の記録ですが、同じ業務transactionとして対応づく必要があります。
顧客向け履歴では、現在のmasterだけでなく取引時点のsnapshotが説明に必要です。何を正規化し、何を取引へ固定するかは時間軸から判断します。
体験学習の続き · LEARNING LOOP
ここまで読めましたか?続けて確認してみましょう
Aggregate・Snapshot・Projectionの考え方を読んだところで、教材内の参照元で実際のテーブル定義を確認し、自分の言葉で説明してみます。
- 02仕組みを確かめる — CONCEPT CHECK
画面操作なしで、仕組みを確かめる
この仕組みは、Mini Bankの画面には現れません。次のEvidenceとCode Trail、教材本文だけで理解を進めます。
- 03証拠を確認する — EVIDENCE
何を見たか、1つずつ確かめる
Evidence(判断の根拠となる記録)を確認したらチェックします。各項目の「何を見るか」を手がかりにしてください。
画面を開けない項目は、Source Trail(教材の参照元)と教材の説明で確認できます。失敗を実際に起こす必要はありません。
- 04コードの処理経路を追う — CODE TRAIL
コードを書く必要はありません
ここでは「説明されている仕組みが、実際のMini Bankのどこに実装されているか」を確認します。まずは日本語の役割説明を読み、入力した依頼がどこへ渡るかをつかみましょう。コードの全文を読む必要はありません。
まず定期預金の契約テーブルを見て、次にLedger accountとの関係を見ます。契約記録と会計残高がどう対応づくかを確かめましょう。
教材の参照元を確認する — Source Trail ↓定期預金の契約modelとpooled account。契約別Subledgerがどう作られるかを探します。
supabase/migrations/20260824130001_create_time_deposits.sqlMigration(DB定義の変更履歴)。口座とledger accountの関係を確認します。
supabase/migrations/20260819124348_create_ledger_accounts.sql - 05自分の言葉で説明する — EXPLAIN
LEVEL 1 — やさしい説明(まずここだけ)
なぜ契約ごとのtime_depositsと、会計上のTIME_DEPOSITS_PAYABLEという勘定を分けて設計しているのでしょうか? 短い2文を目安に説明してください。
日常語だけで完了できます。専門用語の数や正誤を自動採点するものではありません。
ヒント1を見る
「契約」と「勘定」という2つの言葉を使って書いてみましょう。
ヒント2を見る
なぜ2つを分けて設計する必要があるのか考えてみましょう。
もっと詳しく:LEVEL 2 — 専門用語を使った説明(任意)
Aggregate、Snapshot、Projectionの3つを使って、データモデルが現実の契約・状態・証拠をどう表すか説明してみましょう。
挑戦する場合は、上の説明欄に追記できます。追加しなくても完了できます。
説明の確認ポイント — Checkpoint- 契約記録と会計残高が別の責任を持つと説明できる
- cached_balanceが正式記録ではなく照合対象の派生値だと説明できる
理解を確かめる — CHECK YOUR UNDERSTANDING
3問で理解チェック
答えを頭の中で考えてから、各項目を開いて確認してください。
Q1画面に表示する値はすべてmaster参照でよいですか?
いいえ。過去取引の説明には取引時点のsnapshotが必要な場合があります。
Q2状態機械で状態名以外に必要なものは?
許可遷移、実行者、前提、競合時の勝者、終了状態の再実行・取消方針です。
Q3cached_balanceを持つのは誤りですか?
誤りではありません。正式記録をLedgerと定め、照合・再計算できるなら有効なprojectionです。
完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。
教材の参照元 — SOURCE TRAIL
根拠と関連教材
CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。
確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。
- 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260824130001_create_time_deposits.sql定期預金の契約modelとpooled account - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260823170001_create_transfer_beneficiary_and_otp.sql振込依頼・OTP・振込先snapshot - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260819124348_create_ledger_accounts.sql口座とledger accountの関係