Uso personale

Finora — un'app di finanza personale costruita per come si usano davvero i soldi

App di finanza personale multi-conto e multi-valuta con consulente AI self-hosted, costruita per rispondere a domande reali sulla gestione dei propri soldi.

Progetto privato — in uso personale
  • React
  • TypeScript
  • Vite
  • Capacitor
  • Node.js
  • Express
  • PostgreSQL
  • Ollama
Finora — un'app di finanza personale costruita per come si usano davvero i soldi

Il problema

Le app di finanza personale che avevo provato mi facevano scegliere tra due estremi: fogli Excel infinitamente flessibili ma che non tenevo mai aggiornati, oppure app chiuse che aggregano i conti bancari ma trattano ogni voce come un numero isolato — nessun contesto su progetti, obiettivi, persone con cui dividere una spesa, o su come una spesa di oggi si ripercuote sui prossimi mesi.

Ho iniziato a costruire Finora per uso personale, con un vincolo preciso: ogni funzionalità doveva rispondere a una domanda concreta che mi ero fatto gestendo i miei conti ("quanto posso ancora spendere questo mese?", "questo abbonamento lo sto ancora usando?", "chi mi deve ancora dei soldi per la cena di sabato?"), non a un'idea astratta di "cosa fa un'app di budgeting".

Perché l'ho costruito così

Non partivo da un design system o da un mockup: partivo dal mio comportamento reale con i soldi. Questo ha guidato scelte che altrimenti non avrebbero senso in un MVP:

  • Multi-conto e multi-valuta fin dal modello dati, perché ho conti in due valute diverse e un estratto conto trimestrale in un'altra ancora.
  • Progetti (Aggregatori) come entità trasversale alle categorie, perché "vacanza in Giappone" taglia spese di trasporti, alloggio e ristoranti — categorizzarle non basta, serve un contenitore con un budget proprio.
  • Un motore di ricorrenze reale con scheduler, non solo un promemoria, perché affitto, stipendio e abbonamenti determinano quanto sono libero di spendere prima ancora di guardare il saldo attuale.

Architettura

Un solo frontend React + Vite (PWA, TanStack Query, Tailwind, shadcn/ui) serve tre target: browser desktop, PWA installabile su mobile, e — tramite Capacitor — un progetto Android nativo vero e proprio, per avere accesso a notifiche push e comportamento offline che il solo service worker non garantisce in modo affidabile su tutti i dispositivi. Comunica via JWT (access + refresh) con un'API REST Express (28 route module).

Il backend è un monolite Express deliberatamente non-microservizi: 28 route module, circa 8.700 righe, un solo database Postgres con 29 modelli Prisma, uno scheduler interno (cron) e integrazioni verso Yahoo Finance, CoinGecko, Frankfurter (BCE) e un'istanza Ollama self-hosted. Per un progetto con un utente (io) e poi un numero contenuto di famiglie/coppie in modalità condivisa, la complessità operativa di microservizi non si sarebbe ripagata: preferisco poter fare una query con una join invece di orchestrare chiamate tra servizi.

Architettura

Client

React + Vite PWA

+ Capacitor (Android nativo)

Backend

Express API

28 route module

Dati

PostgreSQL

AI

Ollama self-hosted · qwen2.5

Servizio esterno

Yahoo Finance

CoinGecko

Frankfurter (BCE)

React + Vite PWAExpress API· JWTExpress APIPostgreSQLExpress APIOllama self-hosted · qwen2.5Express APIYahoo FinanceExpress APICoinGeckoExpress APIFrankfurter (BCE)

Feature che raccontano delle scelte tecniche precise

  • Multi-valuta senza perdere la verità storica: ogni transazione salva sia l'importo convertito nella valuta base sia l'importo originale, la valuta originale e il tasso di cambio usato — recuperato storicamente (non al giorno corrente) tramite l'API della BCE (Frankfurter). Così un rendiconto di marzo non cambia retroattivamente se il cambio EUR/USD si muove ad aprile.
  • Prezzi di portafoglio sempre aggiornati, senza far pagare l'utente in richieste API: gli investimenti (ETF, azioni, crypto, obbligazioni) si auto-aggiornano tramite Yahoo Finance e CoinGecko con un cron nei soli orari di mercato (lun–ven 8–22), non ogni minuto — un compromesso deliberato tra freschezza dei dati e rispetto dei rate limit dei provider gratuiti.
  • Un motore di regole di auto-categorizzazione, non hardcoded: l'utente definisce condizioni (description contains "Esselunga") e azioni (set_category → Spesa) con priorità, valutate come una vera pipeline — oltre a un secondo livello di suggerimento basato sullo storico personale delle proprie transazioni.
  • Import estratto conto da CSV con parser dedicato per il formato dell'aggregatore bancario che uso (due sezioni nello stesso file: transazioni e trasferimenti, date in due formati diversi), con deduplica e riconciliazione contro i saldi già presenti.
  • Un consulente finanziario AI che non inventa numeri: ho collegato un modello Ollama self-hosted (qwen2.5, su un server in LAN) invece di un'API cloud a pagamento, i dati finanziari non lasciano mai la mia infrastruttura. Il vincolo tecnico più interessante non è stato il collegamento al modello, ma il prompt engineering: al modello viene passato solo un blocco di dati già calcolati dal backend (saldo, spese medie, budget, obiettivi con giorni residui) con un'istruzione esplicita a non stimare cifre non presenti nel contesto — per evitare il rischio più concreto di un LLM su dati finanziari, l'allucinazione di un numero plausibile ma sbagliato. Ho anche dovuto tarare num_ctx esplicitamente (il default di molti modelli tronca in silenzio il contesto) e un timeout di inattività sullo streaming invece di un timeout fisso, per non tagliare risposte lunghe ma ancora attive.
  • Divisione spese e saldi tra persone, per gestire spese condivise (una cena, un affitto) senza dover per forza modellarle come un "conto congiunto" — ogni spesa può avere split multipli con persone esterne all'app, marcati come saldati o meno.
  • Sicurezza: JWT a due token (access 15 minuti + refresh 30 giorni con rotazione), 2FA opzionale via TOTP o codice email, cancellazione account con periodo di grazia (soft-delete + cron di retention a 90 giorni che ripulisce anche token scaduti e codici 2FA inutilizzati) invece di una DELETE distruttiva immediata.
  • Shared Spaces: modalità famiglia/coppia con ruoli (owner/editor/viewer) e invito via codice, per condividere conti e budget senza dover condividere una singola utenza.

Numeri

  • ~32.500 righe di codice (8.700 backend, 23.800 frontend), tutto TypeScript stretto lato client.
  • 29 modelli dati, 28 moduli di route REST.
  • 6 job schedulati (ricorrenze, interessi, prezzi, PAC, rate prestiti, retention dati) con logging centralizzato e un endpoint admin per rilanciarli manualmente.
  • 4 lingue supportate in UI (it/en/fr/de), 23 valute con tasso storico.
  • App installabile come PWA e buildabile come app Android nativa dallo stesso codice frontend.

~32.500

righe di codice

29

modelli dati

28

moduli route REST

23

valute supportate

Cosa ho imparato

La lezione più utile non è tecnica ma di scoping: costruendo per me stesso, la tentazione era aggiungere feature "perché sarebbero fighe" (l'advisor AI, la mappa delle spese). Il filtro che ha funzionato è stato chiedermi ogni volta se avrei effettivamente cambiato comportamento avendo quel dato sotto mano — non se il dato era interessante da guardare. Le feature che uso di più oggi (spending power, ricorrenze, budget per categoria) sono anche le più "noiose" da presentare in un pitch.

Sul piano tecnico, il vincolo più formativo è stato progettare il modello dati fin dall'inizio per multi-valuta e transazioni atomiche (trasferimenti come prisma.$transaction, mai un semplice update a due tabelle separate) — tornare indietro su quello dopo aver accumulato dati reali sarebbe stato molto più costoso che farlo bene al primo giro.

Screenshot

Modalità:
Dashboard principale con saldo e spending power
Lista progetti/budget (Aggregatori)
Dettaglio di un Aggregatore, es. un viaggio
Andamento del portafoglio investimenti
Chat con il consulente finanziario AI
Consulente AI, stato iniziale
Motore di regole di auto-categorizzazione
Divisione spese condivise tra persone