真北科技Zhenbei
legdger · データモデル

残高をなぜ永続化すべきでないか

残高をフィールドとして保存すれば、変更のたびに同期して維持しなければなりません。これがデータ不整合の最大の発生源です。複式簿記はもっと気の楽な答えを与えてくれます。

ほとんどの家計簿アプリは、残高をウォレットテーブルの balance フィールドに保存します。支出を 1 件書き込むとき、コードでは 2 つのことを行います。取引を挿入し、次に残高を更新します。この 2 つは同時に成功しなければならず、そうでないと帳簿が合わなくなります。

単一マシンのシングルスレッドであれば問題ありません。しかし同期、インポート、一括消し込み、並行書き込みが絡むと、残高フィールドの維持は終わりのない防御戦になります。変更のたびに「残高は更新されたか、正しく更新されたか、どのコードパスで更新し忘れたか」を考えなければなりません。

発想の転換:残高を導出値にする

複式簿記のやり方では、1 件の記帳はひとつの金額ではなく、Leg のグループです。各 Leg は「袋 + 金額」です。核心の制約はただひとつ。同じ取引内で、通貨ごとの Leg の合計はゼロでなければならない。

LedgerCore — Leg の合計はゼロ
01// 一条账目 = 一组求和为零的 Leg
02// 午饭 38 元,用招行卡支付:
03transaction(term: .expense) {
04 leg(from: .asset(.cmbCard), amount: -38.00)
05 leg(from: .expense(.dining), amount: +38.00)
06} // sum == 0 ✓
07
08// 余额不存库,永远派生:
09// balance = 期初 + Σ 该袋子下所有 Leg

残高はこれで、維持すべき状態ではなくクエリの結果になります。取引の書き込みで保証すべきは Leg の合計がゼロであることだけ。これはカーネル層のひとつの検証ゲートで強制できる性質のものです。

袋モデル:11 種類で足りて、膨らまない

資産、負債、証券、暗号資産のポジション、消し込み待ち、分割の中間袋など。11 種類の袋型が、個人記帳で実際に遭遇するシナリオをカバーします。袋型は固定の列挙型で、ユーザーによる独自定義は許しません。これは意図的な収束で、データモデルが要件とともに際限なく膨らむのを防ぎます。

代償は何か

  • 残高の照会には集計計算が必要で、単一フィールドの読み取りより遅い(インデックスとキャッシュで補う)
  • データ量が増えると集計コストが上がるため、期間ごとに分割して管理する必要がある
  • 開発者への要求が高まる。書き込みのたびに正しい Leg の組み合わせを構成する必要があり、数字だけを書き換えることはできない

私たちは「書くのが面倒な」モデルと引き換えに、「残高が合わない」という問題のカテゴリごと排除しました。記帳ツールにとって、この取引は十分に割に合います。

More Notes
Get in touch

アイデンティティ・エージェント・プライベートドメインの複雑さを、統治可能なひとつのカーネルへ

既存の IAM の置き換え、エージェント基盤の構築、あるいはプライベートドメイン運用の本格化——まずは 30 分のアーキテクチャ相談から始めましょう。私たちが得意とする種類の問題かを先に判断し、適さない場合は率直にお伝えします。