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.
- React
- TypeScript
- Vite
- Capacitor
- Node.js
- Express
- PostgreSQL
- Ollama

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)
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_ctxesplicitamente (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.