WorkMindVault – Multi-User Personal Finance Ledger Platform~6 min read
Full-Stack FinTechCompletedProprietarySolo Developer · Self-Learning Project

MindVault – Multi-User Personal Finance Ledger Platform

Full-stack personal finance app with an immutable decimal-precise ledger, per-account roles and pre-aggregated analytics, built as a self-directed learning project.

Role:Solo Full-Stack Engineer
Timeline:Oct 2025 — Dec 2025
Next.js 15NestJS 11PostgreSQLTypeScriptTypeORM+1

Designed and built the entire platform alone, backend and frontend, to learn production-style NestJS, PostgreSQL and Next.js architecture.

My contribution

Solo Developer · Self-Learning Project

Designed and built the entire platform alone, backend and frontend, to learn production-style NestJS, PostgreSQL and Next.js architecture.

What I owned

  • NestJS 11 backend: 11 modules, 22 controllers, 28 services, 24 TypeORM entities
  • Arbitrary-precision ledger engine and incremental summary pipeline
  • JWT, passkey and security-question auth with session tracking
  • Account-level RBAC (Owner, Admin, Editor, Viewer)
  • Next.js 15 App Router client: 32 pages, 11 Zustand stores, 10 API services
  • Seeder CLI, Docker setup and 82 automated test cases

Out of scope

  • No production deployment or real end-users (learning project)

Product context

Individuals & Families Managing Multiple Accounts

Exact balances, auditable history, per-account sharing.

A self-built, multi-user personal finance platform that keeps balances exact, edits auditable and sharing granular, with fast analytics from pre-aggregated summaries.

Core challenge

Evidence: assumption

Personal finance tools suffer from floating-point balance drift, opaque balance edits, all-or-nothing sharing and dashboards that slow down as transaction history grows.

Who hurts:
individual-account-ownershared-account-collaborator

User benefit matrix

Strategyzer Canvas

The Value Proposition Canvas

Value PropositionMindVault Personal Finance Ledger
Customer SegmentIndividuals & Families Managing Multiple Accounts
Expand value canvas
Products & Services
Gain Creators
Pain Relievers
Gains
Pains
Customer Jobs

Sector

Title

Body

Individual Account Owner

Balances that drift or change without a trace

designed
Before

Floating-point arithmetic and in-place edits make reconciliation unreliable.

After

decimal.js arithmetic with non-negative balance checks and an append-only AccountLedger audit trail.

Metric: Balance arithmetic
IEEE 754 floating-point→Arbitrary-precision decimals
Ledger EngineAccountLedger Audit Trail
personal
Shared-Account Collaborator

Sharing means giving full access or duplicating accounts

designed
Before

A single global role applies to every account a user touches.

After

Per-account roles (Owner, Admin, Editor, Viewer) enforced by NestJS guards via AccountUserRole.

Metric: Permission scope
Global role only→Per-account role
Account RBACRole Guards
personal
Dashboard Viewer

Analytics slow down as history grows

designed
Before

Trend charts run SUM and GROUP BY scans over all transactions.

After

Daily, weekly, monthly, yearly and category summaries updated transactionally on each write.

Metric: Analytics read path
On-demand aggregation over raw transactions→Reads from 6 pre-aggregated summary entities
Summary EngineTrend Charts
personal

Build

Stack and engineering choices

Technology

Frontend
  • Next.js 15 (App Router)
  • React 19
  • TypeScript 5
  • TailwindCSS 4
  • ShadCN UI (Radix UI)
  • Zustand 5
  • Recharts
  • Chart.js
  • React Hook Form
  • Zod
Backend
  • NestJS 11
  • TypeORM 0.3
  • Passport JWT
  • bcrypt
  • decimal.js
  • class-validator
  • Swagger
Database
  • PostgreSQL
  • 24 TypeORM Entities
DevOps & Infra
  • Docker
  • Docker Compose
  • pnpm

Key decisions

Personal finance tools suffer from floating-point balance drift, opaque balance edits, all-or-nothing sharing and dashboards that slow down as transaction history grows.

JavaScript floating-point arithmetic makes account balances drift over repeated transactions.

Decision: Stored amounts as string/numeric values and wrapped all math in decimal.js helpers (safeAdd, safeSubtract) with non-negative balance checks. Trade-off: explicit type parsing across services and DTOs.

Outcome: Exact monetary arithmetic and no negative-balance underflow in ledger operations.

Multi-interval trend queries need expensive SUM and GROUP BY scans as history grows.

Decision: Added dedicated summary entities (daily, weekly, monthly, yearly, category) updated transactionally on create, update and delete. Trade-off: extra write-path work.

Outcome: Dashboards and comparisons read pre-aggregated rows instead of scanning raw transactions.

Users need different permissions on different accounts (owner here, viewer on a family account).

Decision: Separated global authentication from account authorization with an AccountUserRole junction entity and custom NestJS guards. Trade-off: extra lookups on account-scoped requests.

Outcome: Strict per-account access without duplicating accounts or raising global privileges.

Multi-entity client state and inconsistent API feedback caused prop drilling.

Decision: Split data access into 10 API services and state into 11 domain Zustand stores, with a global ApiResponseToast bridge. Trade-off: more store and service files.

Outcome: No prop drilling, isolated re-renders and uniform feedback on every mutation.

Show 1 more decisions

Tagging needed soft delete and grouping without bloating controllers.

Dedicated repositories, validators and transformers for tags and tag groups. Trade-off: more classes per module.

A decoupled tagging subsystem with recycle-bin restore.

Trust

Security and roadmap

Security & reliability

  • JWT cookie and bearer authentication with bcrypt password hashing.
  • Passkey verification, security questions, active session tracking and password-reset logging.
  • Account-scoped RBAC guards (Owner, Admin, Editor, Viewer) on top of global authentication.
  • Global ValidationPipe across 44 DTOs.
  • Non-negative balance enforcement in the decimal ledger engine.
  • 82 automated test cases across 10 spec files (unit and e2e).

Future roadmap

  • Financial statement export engine (PDF and CSV).
  • Personal growth suite: LifeLog, Contacts, Capsule reflection, Knowledge base, Micro Actions, Storyline.
Progressive Disclosure

Architectural Deep-Dive, Schemas & Workflows

Jump:

System Architecture & Domain Scope

MindVault is a multi-user personal finance platform I built solo as a self-directed learning project, to practice production-style backend and frontend architecture rather than to serve live users. The NestJS backend owns accounts, transactions, the ledger, summaries, tags and authentication; the Next.js client handles dashboards, reports and account management.

LayerFlow
FrontendPage (app/) → Zustand Store → API Service → HTTP
BackendController → ValidationPipe (DTO) → Service → TypeORM → PostgreSQL
flowchart TD
    subgraph Client ["Next.js 15 App Router"]
        UI["32 pages · 92 components"]
        Stores["11 Zustand stores"]
        Services["10 API services"]
        UI --> Stores --> Services
    end

    subgraph Server ["NestJS 11 + TypeORM"]
        Controllers["22 controllers · 89 endpoints"]
        Guards["JWT & account RBAC guards"]
        Domain["28 domain services"]
        Ledger["Ledger & decimal engine"]
        Summary["Incremental summary engine"]
        Controllers --> Guards --> Domain
        Domain --> Ledger
        Domain --> Summary
    end

    DB[("PostgreSQL · 24 entities")]
    Services -->|"REST + cookies"| Controllers
    Domain --> DB

Persona Value Map

PersonaPains removedBuilt
Account OwnerBalance drift, untraceable editsDecimal ledger, immutable AccountLedger, non-negative checks
CollaboratorAll-or-nothing sharingPer-account roles via AccountUserRole and guards
Dashboard ViewerSlow analytics, lost recordsPre-aggregated summaries, charts, soft-delete recycle bin

Core Engineering Innovations

1. Precision Ledger Engine

Balances are updated through safeAdd and safeSubtract helpers on decimal.js, with non-negative checks, while every change is written to an immutable AccountLedger table for audit.

2. Incremental Summary Pipeline

Transaction create, update and delete hooks update 6 summary entities (daily, weekly, monthly, yearly, category) in the same transaction, so analytics endpoints read rollups instead of raw history.

3. Contextual Account RBAC

An AccountUserRole junction entity maps users to accounts with Owner, Admin, Editor or Viewer roles, enforced by custom NestJS guards independent of global JWT auth.

4. Client State Architecture

Domain-specific Zustand stores and API services feed an ApiResponseToast listener, giving consistent success and error feedback across all mutations.

Database Design & Concurrency

  • 24 TypeORM entities on PostgreSQL covering users, sessions, accounts, ledgers, transactions, summaries, categories, currencies, roles and tags.
  • Summary updates run in the same transaction as the ledger mutation they derive from, keeping balances and rollups consistent.
  • Six nestjs-command seeders bootstrap roles, categories, currencies, account types, tag groups and tags.
  • Tags use soft delete with restore, plus dedicated repositories, validators and transformers.

Performance Benchmarks & Reliability

MetricValue
REST endpoints89 across 22 controllers
Domain services28
Database entities24
DTOs44
Automated tests82 cases across 10 spec files
Frontend routes / components32 pages / 92 components
Zustand stores / API services11 / 10
Commits161 (100 backend, 61 frontend), solo

Lessons Learned & Architectural Retrospective

  • What worked: isolating money math in one helper module and routing every balance change through it kept precision rules in a single place.
  • Trade-offs: pre-aggregated summaries speed up reads but add write-path work; per-account roles add joins on scoped requests.
  • Learning outcome: this project was my practice ground for modular NestJS design, transactional consistency, role-based access and a store-per-domain frontend.
  • Next: statement export (PDF and CSV), then the personal growth suite.

Case closed

More work.