where did it go
Where did it go? — the exact thing I ask every time I open my banking app. So I named the tool after the question.
What even is this?
Where Did It Go is a personal finance tracker: wallets, transactions, categories, and a monthly history that tells me where my money actually went instead of leaving me to guess. The API is a .NET clean-architecture backend that owns all the money math, and a separate Nuxt front-end is where I actually log spending.
It's not a budgeting app with charts and confetti. It's a plain ledger that answers a plain question — and I built it because I kept failing to answer that question for myself.
Why I built it
I wanted to be financially responsible, and "being responsible" turned out to mean knowing where the money goes. I could've downloaded any of a hundred expense apps that do this better. But they all track money the way they think about it — their categories, their idea of a wallet, their subscription nag screens.
I wanted a ledger shaped around how I spend, one I fully control and can extend whenever a new habit shows up. So I built the boring version: no gamification, no "insights," just an honest record. Turns out the honest record was the whole point.
How it works
The split is deliberate, and mostly along "who owns the truth":
- I log a transaction from the Nuxt front-end.
- The request hits the .NET API, where a command runs through validation and lands in the domain.
- The domain layer does the money math and enforces the rules — you can't spend from a closed wallet, amounts are real
Money, not loose decimals. - Reads like this month's history go through a query that can lean on Redis instead of hammering Postgres every time.
One backend owns the rules. The front-end just asks nicely.
What I learned
Money is not a decimal. My first instinct was to slap decimal Amount on the transaction entity and move on. Then I remembered that money has a currency, has rounding rules, and has no business being added to a bare number somewhere three layers away. So I gave it a proper Money value object. It felt like over-engineering for a personal app right up until the moment it quietly stopped me from doing something dumb.
I wrote my own MediatR — on purpose. The app leans hard on CQRS: every action is a command or a query with its own handler. The obvious move is to reach for MediatR. Instead I hand-rolled a tiny Dispatcher with my own IRequest/IHandler interfaces. Partly it was the licensing drama around the library, partly I just wanted to understand the pattern instead of importing it. It's maybe a hundred lines and it does exactly what I need — nothing more. Reinventing the wheel, again. Worth it, again.
Clean architecture pays you back later, not now. Splitting a personal tracker into Domain / Application / Infrastructure / API felt like cosplaying an enterprise codebase for a project only I will ever run. But every time I added a feature — a new query, a wallet status, a validation rule — I knew exactly which layer to open. The upfront ceremony bought me a codebase I can still reason about months later. Past me being annoying for present me's benefit.
Stack
- Backend — C#, .NET, minimal APIs, hand-rolled CQRS dispatcher
- Frontend — Nuxt (private repo — it's my own wallet in there)
- Data — PostgreSQL via EF Core, Redis for reads
- Infra — Docker Compose