ドメインモデリングをやってみた〜袋分け家計簿系YouTuberみたいな家計簿アプリ〜

2025-12-06

あいさつ

早12月も一週間が経とうとしています。もう年の瀬が近づいてきました。この時期の楽しみといえば、先週にあったブラックフライデーでしょうか。みなさんもお買い物はしましたか?私は今年はついにAmazon Echoを購入しました。早速Alexaの機能をしらみつぶしに試して遊んでいます。ささやき声で話しかけると、ちゃんとささやき声で返してくれてかわいいですね。そのうちAlexaのスキルも作って遊んでみたいですね。

気の赴くままに書いていたらポエム部分含め非常に長くなってしまいましたmm。お時間のない方は生成AIにでも食わせて読んでやってください。

家計簿を作りたい

ところで、ブラックフライデーのお買い物の時に、一つ困ったことがありました。それは、”今自分がいくら使っていいのかがわからない”ということです。

私のメインの銀行口座には、何かあった時のために置いておきたい一定の資金+お買い物に使ってもいいお金が入っています。しかし、お買い物に使ってもいいお金を全てブラックフライデーにベットしていいわけではなく、将来欲しい高額なもの(例えばカメラ)のためにとっておきたかったりするわけです。(あと、実家暮らしなので家賃光熱費水道代という概念がないです……)

また、今はインターンやバイトがあったりなかったりして収入が増減するため、”来月はバイトがないから来月の外食費を取っておきたい”といった目的のお金として置いてあったりします。

つまり、“今自分がいくら使っていいかわからない”というのは、銀行口座のうち、”お買い物に使っていいお金”の目的の内訳がわからないということです。

そこで注目したのが、袋分け家計簿系YouTuberです。彼/彼女らのルーティンは、Geminiに解説してもらいましょう:

はい。こんな感じで給料を現金で引き出して、食費、生活費など、目的ごとに(オシャレに)袋分けしている人のことを指しています。

あまりオシャレさには興味がないのですが(((、私もこういう目的別にわけるお金の管理をしたいと考えました。しかし、圧倒的な問題点として、キャッシュレス派なので現金を引き出して袋にわけるなんてことはやってられません。つまり、何らかのアプリ上で、自分が持っている預金の用途を可視化できるようなものがあるといいわけです。

まず考えたのが、5年ほど家計簿アプリとして使っているMoneyForward MEの活用ですが、MoneyForwad MEの家計簿機能だけではこのような運用は難しいです。

他のデジタル上で袋分けしてお金を管理できるソリューションとして、みんなの銀行 の”ボックス”や、ワンバンク(旧B43) のカードを発行すると使える”ポケット機能”があります。後者は結構ガチで袋分けのことを考えて作られてそうなのでいいなとは思いました。が、しかし、前者は今のメインバンクとの間の振り込み手数料、後者のカードを持ってしまうと某ECサイトでのポイントが今のクレカよりつかなくなるという点がネックです。

また、目的別の貯金という観点では、Finbeeというアプリもありましたが、これも普通口座から貯金用口座に資金を移動させるという特性上、貯金用口座そのものに対応している銀行が少なく、自分の使っている銀行以外に口座を作らないといけないのがネックでした。

…つまり…!!!これは個人開発のチャンスなのです!!!!(勢い())

そしてできたのがこちら!!!!…………………と言いたいところですが全然出来上がりませんでした。家計簿って難しいっすね。

代わりにここからは、どのようにアプリの(主にバックエンドの)設計を行ったのか、具体的にはアプリの中のドメインモデルをどのように考えたのかというところをお話しします。

ドメインモデリング

今回はこちらのドメイン駆動設計 モデリング/実装ガイド』を参考にモデリングを行いました。

DDDとは?ドメインモデルとは?についてはこちらの書籍をご覧ください。

ユースケースの一覧

まずはユースケースの一覧を考えてみました。袋分け家計簿YouTuberの観点は主に収入をどうやって分配するか、という部分でしたが、家計簿として使う以上支出についても管理できるようにします。また、私自身が立て替えを行うことが多く、その管理もしたかったのでユースケースに入れてみました。

ユースケース名説明
収入を袋へ配分する食費・日用品など週単位/カテゴリー単位で袋へ割り当てる
目的別積立を作る・積み立てる車検・旅行などの特別費用に向け積立を行う
予備資金を一定額まで確保する安全資金を設定し、不足分を袋に確保する
袋から支払いを行う現金・クレカ支払いを袋残高に反映する
袋間で振替を行う飲みすぎ・使いすぎ時の予算調整
立て替えを記録し精算する他者の支払いを立て替え、後で回収

ちなみに、この表はチャッピーに出してもらいました。私のチャッピーはこちらのプロンプトを参考にしてギュインギュインに論理的なヤツになっております。あとはチャット上で「あなたは簿記一級を持った凄腕アドバイザーです」と言ってみました。

ドメインモデルの作成

今回は、この全てのユースケースを対象に、ドメインモデルを考えていこうと思います。 ここでは本にあるように、次のルールでドメインモデルを作っていこうと思います。

- オブジェクトの代表的な属性を書くが、メソッドまで書かなくてよい
- 「ルール/成約(ドメイン知識)」を吹き出しに書き出す
- オブジェクト同士の関連を示す
- 多重度を定義する
- 集約の範囲を定義する
- 理解を促進するために、具体例などを書いても良い

ドメイン駆動設計 p.23より引用

ドメインモデルを作る前に、ユビキタス言語を定義しておきます。ユビキタス言語とは、会話でも、ドキュメントでも、コードでも使う、開発者とドメインエキスパートとの間の共通言語のことです。

用語定義(アプリ内での意味)会計上の位置づけ(資産/負債/費用…)
袋(Envelope)ユーザーが「ここにいくらある」と認識する仮想口座通常は資産(現金の内部振替先)
予備袋常に○万円キープしておきたい緊急用の袋資産
積立袋車検・旅行など将来の特別支出のための袋資産(目的制約付き)
立替袋他人の支払いを立て替えた分を記録する場所原則「他人に対する債権(資産)」
収入給与や小遣いなどの増加するお金収入
支出実際にお金が外部に出て行った取引費用/資産の減少
振替袋間の内部移動。資産内の付け替え資産勘定間の振替
取引(Transaction)1回の経済的イベント。必ず複式で記録される
貸方(Debit)資産が減る方向
借方(Credit)資産が増える方向

今回は、立て替えなどちょっと複雑な会計も正しく記録できるように、(ChatGPTに教えてもらいつつ)複式簿記という方法を参考にしながらドメインモデルを設計しました。 単式簿記はお小遣い帳のようなシンプルな家計簿のイメージ、複式簿記は企業がつけているきっちりした帳簿のイメージです(?)。 (ちなみに、行政の会計は単式簿記が使われており、さすがに複式簿記でやった方がいいのではって言っている人もいるらしいですね) ↓詳しい説明by ChatGPT

先ほどのユースケースの一覧を見ながらオブジェクトの一覧を考えていきます。こちらも先ほどの厳しいChatGPTにレビューしてもらいながら仕上げていきました。 考えたポイント

  • Envelopeの残高

      直感的に考えるとEnvelopeに残高を持たせたいところですが、そうするとTransactionと二重で値をもつことになり不整合が発生する可能性があります。そのため、計算のコストがかかってしまうのですがTransactionから算出することにしました。(こういう場合、Transactionが膨大な量になったらどうしたらいいんでしょうか…この設計はそのままに、どこかに計算がキャッシュできるといいんですが)

  • Envelopeの残高がマイナスになるTransaction

      Envelopeの残高を素直にマイナスにするのと、エラーを出して支出ができないようにする2パターンが考えられます。今回は使い過ぎを防ぎたいという目的のため、エラーを出す方を選ぶ。他のEnvelopeから資金を移動させないと払えないようにしました。

  • 外部からのTransaction

      バイト先からの収入では、借方(debit, お金が増える方)が自分の袋、貸方(credit, お金が減る方)がバイト先のお財布になります。しかし今回はバイト先のお財布をソフトウェアの中でモデリングしないため、nullを許容する設計としました。 しかし、envelopeの参照エラーでnullになると区別がつかないので、貸方が外部の場合はnullの代わりに特別なオブジェクトを用意するとか別の方法を考えたようないい気がします。。。

また集約「家計簿」HouseholdBudgetについては以下のように定義しました。今回は実際の銀行口座は考えていなかったので、アプリケーション内ではこの集約のみになりますかね。

ルートエンティティ

  • User

集約に含まれるオブジェクト

  • User
  • Envelope
  • Transaction
  • Category(これも実質このユーザーの家計設定なので含めてよい)
💬 ルール / 制約 ・この集約内のTransactionは、必ず集約内のUser, Envelope, Categoryを参照する ・Envelopeの残高は、配下のTransactionから一意に計算できる状態を保つ(balanceを持つなら、「Transactionから計算した結果と常に一致する」こと) ・異なるUserのEnvelope/Transaction/Categoryを跨いだ取引は存在しない

最後に、テストとして、先ほど作ったユースケースに沿ってそれぞれのドメインモデルにどのような値が入るかシミュレーションしてみます。 貸方(debit, お金が増える方)・借方(credit, お金が減る方)の概念がこんがらがるところですが、厳しいチャッピーに教えてもらいながらやりました。

① 収入を袋へ配分する

給与の一部 5,000 円を「食費1週目」へ取り分ける例
amount: 5000
debitEnvelopeId: "env_food_w1"     // 袋が増える
creditEnvelopeId: null             // 外部の収入勘定
categoryId: "cat_salary"
memo: "給与の一部を食費用に振り分け"

② 袋から支出する

食費1週目の袋から 1,000 円を支出
amount: 1000
debitEnvelopeId: null              // 費用勘定側(外部)
creditEnvelopeId: "env_food_w1"    // 袋が減る
categoryId: "cat_food"
memo: "スーパーで食材購入"

残高が足りない場合はエラー → 他袋から振替してから支出させる。


③ 袋間振替

予備袋 → 食費4週目 へ 3,000 円移動
amount: 3000
debitEnvelopeId: "env_food_2025w4"   // 振替先(増える)
creditEnvelopeId: "env_reserve"      // 振替元(減る)
categoryId: null
memo: "食費が不足したため予備から補填"

④ 立替(発生)

友人の飲み代 2,000 円を現金で立替えた
amount: 2000
debitEnvelopeId: "env_advance"     // 債権(立替袋)が増える
creditEnvelopeId: "env_wallet"     // 使った現金が減る
categoryId: null
memo: "友人Aの飲み代を立替"

⑤ 立替(回収)

友人から返金されたとき
amount: 2000
debi

最後に

思ったよりも記事が長くなってしまいました。最後まで目を通してくださってありがとうございました。

最初はなんとなくノリでドメインモデルが作れるだろうと思っていたのですが、ちゃんとやろうとすると複式簿記の概念が必要そうです。今までなんとなくクレジット・デビットという言葉を使っていましたが改めて意味が明確になって勉強になりました。この辺の基礎知識は簿記3級の範囲らしいので、簿記の勉強をしてから考えるとより穴がなく設計ができるかもしれません。

不動産系のプロダクトに関わっているソフトウェアエンジニアの方で、宅建を取ろうとしてた人を見たことがあるのですが気持ちがわかってきました。これは開発してたらちゃんとした知識が欲しくなりますね。

みなさんも、自分が詳しいドメインについて、どうやってソフトウェア上でモデリングするか考えてみると楽しいかもしれません!