< Back to projects

Desktop app · case study

Budget Guardian

Personal finance desktop app where every feature runs on custom data structures, no Java Collections, enforced by a build-failing test.

Budget Guardian preview
My role

Sole developer · personal project, built to review DSA before TA work

Type

Desktop application

Stack

Java, JavaFX, SQLite, Maven

Why I built it

I built Budget Guardian to track my own spending, and to review data structures and algorithms before working as a teaching assistant for my university's DSA course.Every feature runs on data structures I wrote myself, with no Java collections in the business logic, and a test fails the build if one appears.I'm the sole developer.

The problem

  • Track spending, budgets, debts and repeat purchases in one desktop app.

  • Review DSA by using each structure where a real feature needs it.

  • Keep the no-collections rule true as the code grows.

Features

  • Dashboard: spend vs. budget, balances, priority alerts
  • Ledger, transfers, and debt tracking with auto-settling payments
  • Detects refillable items and flags overdue purchases
  • Weekly danger-spending guard on Alcohol + Gambling
  • Reports with heap-sort ranking, plus a DFS/BFS transfer graph
  • Free-text search (Ctrl+F) and full undo (Ctrl+Z)

Architecture

Architecture diagramJavaFX views call services that work on an in-memory store of hand-written data structures. Repositories hydrate that store at startup. After that, every change is persisted through them first. Storage is a local SQLite file by default. API mode uses an Express 5 API with zod validation and Prisma on Neon Postgres. The desktop never holds database credentials.DESKTOP APP · JVMAPI MODEJavaFX viewsbuilt in code, no FXMLServicesrule engine, undoevent busIn-memory store8 custom structuresno java.util collectionsRepositoriesone interfacetwo implementationsSQLite filelocal mode, defaultworks offlineExpress 5 APIhelmet, API key, zodauto-started on loopbackNeon Postgresvia Prismamoney as integer satangread, writepersisthydrate at startlocalX-API-KeyPrisma
Dev · applicationSec · security controloptional or pushedScroll sideways to see all of it.
  • JavaFX views to Services
  • Services to In-memory store: read, write
  • Services to Repositories: persist
  • Repositories to In-memory store: hydrate at start
  • Repositories to SQLite file: local
  • Repositories to Express 5 API: X-API-Key
  • Express 5 API to Neon Postgres: Prisma

In words

JavaFX views call services that work on an in-memory store of hand-written data structures.Repositories hydrate that store at startup.After that, every change is persisted through them first.Storage is a local SQLite file by default.API mode uses an Express 5 API with zod validation and Prisma on Neon Postgres.The desktop never holds database credentials.

Pipeline

push / PR → main

  1. mvn test

    incl. ArchitectureGuardTest

  2. Fat jar

  3. Boot smoke test

    launches the real app on Windows

tag v*.*.*

  1. Version from tag

  2. mvn test

  3. jpackage + WiX

    MSI installer

  4. GitHub Release

    one per version, never overwritten

Decisions

Money as whole satang, never floating point.

1 baht is 100 satang.Every calculation is exact, with no rounding drift.

Save first, then update memory.

Every change is persisted before memory changes.A crash or failed save never leaves the app ahead of storage.

Two storage modes behind one interface.

SQLite by default, or an Express API on Neon Postgres.The desktop never holds database credentials.

Transfers are never expenses.

Transfers have their own model and table.They can't distort spending reports.

Danger week is a calendar week.

Monday to Sunday is cheaper to compute than a rolling seven days, and easier to reason about.

Challenges

Keeping the no-collections rule honest.

A test fails the build on any forbidden java.util import.The UI is the one exception: JavaFX needs its own list type.

The cloud database sleeps when idle. The first request is slow.

The client timeout (25s) exceeds the database connect timeout (15s).Only idempotent requests retry.

Shutting the local backend down cleanly on Windows.

It starts Node directly, not through npm.Stopping it can't orphan a process.

Testing

  • 287 JUnit tests pass in Windows CI, from data structures to full-stack integration.

  • A boot-smoke test launches the packaged app and checks every view loads.

  • Version tags publish a permanent MSI.Pushes to main refresh a rolling pre-release.

Outcome

  • v1.0.0 released as a Windows installer.

Next

  • Test coverage reports in CI

  • macOS and Linux installers

Stack

Java

Every feature runs on hand-written data structures.No Java Collections.

JavaFX

Builds the desktop views in code, without FXML.

SQLite

Persists data locally and loads it into memory at startup.

Maven

Builds the fat jar, and the MSI installer with jpackage.

Security

Authentication

None locally: it's a single-user desktop app.API mode authenticates with an X-API-Key header.

Input validation

Services validate every input before saving.The API adds zod schemas.Money is integer satang, never floating point.

Protections

The desktop never holds database credentials.A test fails the build on any java.util collection in business logic.