Nel panorama odierno dello sviluppo software per il settore fintech, la robustezza del codice non è un lusso, ma un requisito di conformità. Quando si progetta un CRM destinato alla gestione di pratiche di credito, l’errore più comune è affidare il ciclo di vita della pratica a una serie disordinata di condizioni booleane. In questo contesto, la Macchina a Stati Finiti (FSM – Finite State Machine) emerge come l’entità architetturale fondamentale per garantire che processi complessi, come l’erogazione di un mutuo, seguano percorsi deterministici e sicuri. Questo articolo esplora come applicare i principi dell’ingegneria dei sistemi per trasformare la logica di business in un motore di workflow inattaccabile, riflettendo l’esperienza maturata nello sviluppo di piattaforme ad alta criticità come il CRM BOMA.
Il Problema della “Spaghetti Logic” nei CRM Finanziari
Immaginate di dover gestire una pratica di mutuo. In un approccio ingenuo, lo sviluppatore potrebbe aggiungere colonne al database come is_approved, docs_uploaded, contract_signed. Il codice risultante per verificare se un mutuo può essere erogato assomiglierebbe a questo:
if (loan.is_approved && loan.docs_uploaded && !loan.is_rejected) {
// Eroga fondi
}
Questo approccio scala disastrosamente. Cosa succede se la pratica viene sospesa? Aggiungiamo un flag is_suspended? E se viene riaperta? La combinazione di N flag booleani crea 2^N stati possibili, la maggior parte dei quali sono stati inconsistenti (es. una pratica contemporaneamente “rifiutata” e “in attesa di firma”). Le macchine a stati finiti risolvono questo problema riducendo l’universo delle possibilità a un grafo diretto di stati validi e transizioni esplicite.
Cos’è una Macchina a Stati Finiti (FSM)?

Una FSM è un modello matematico di calcolo. È un sistema astratto che può trovarsi esattamente in uno di un numero finito di stati in un dato momento. La FSM cambia stato in risposta a degli input esterni; il passaggio da uno stato all’altro è chiamato transizione.
Per un CRM di mutui, una FSM è definita da:
- Stati (S): {BOZZA, ISTRUTTORIA, DELIBERA, FIRMA, EROGATO, RIFIUTATO, ANNULLATO}
- Eventi/Input (E): {INVIA_DOCUMENTI, APPROVA_CREDITO, FIRMA_CLIENTE, EROGA_BONIFICO}
- Funzione di Transizione (δ): Una regola che definisce: Se sono nello stato X e accade l’evento Y, passo allo stato Z.
Modellazione del Workflow del Mutuo

Invece di chiedersi “quali flag sono attivi?”, ci chiediamo “in quale stato si trova la pratica?”. Ecco come modellare un flusso di mutuo standard:
- BOZZA: Il broker sta inserendo i dati. L’unico evento possibile è
INVIA_A_ISTRUTTORIA. - ISTRUTTORIA: La banca analizza i documenti. Eventi possibili:
APPROVA(porta a DELIBERA) oRIFIUTA(porta a RIFIUTATO). Non è possibile andare direttamente a EROGATO. - DELIBERA: Il credito è approvato. Evento:
EMETTI_OFFERTA. - FIRMA: Il cliente deve firmare. Evento:
REGISTRA_FIRMA. - EROGATO: Stato finale positivo.
Diagramma delle Transizioni (Rappresentazione Logica)
La potenza delle macchine a stati finiti risiede nel divieto implicito. Se il sistema riceve l’evento EROGA_BONIFICO mentre la pratica è nello stato ISTRUTTORIA, la FSM non deve limitarsi a fallire silenziosamente: deve lanciare un’eccezione di Transizione Illegale. Questo rende il sistema deterministico.
Implementazione Tecnica: Pattern e Codice
Per implementare una FSM in un moderno stack backend (es. Node.js/TypeScript o Python), sconsigliamo l’uso di switch/case giganti. È preferibile utilizzare il State Pattern o librerie dedicate come XState. Ecco un esempio di implementazione concettuale in TypeScript:
type LoanState = 'DRAFT' | 'UNDERWRITING' | 'APPROVED' | 'DISBURSED' | 'REJECTED';
class MortgageFSM {
private state: LoanState;
private transitions = {
DRAFT: { SUBMIT: 'UNDERWRITING' },
UNDERWRITING: { APPROVE: 'APPROVED', REJECT: 'REJECTED' },
APPROVED: { DISBURSE: 'DISBURSED', CANCEL: 'REJECTED' },
DISBURSED: {}, // Stato terminale
REJECTED: {} // Stato terminale
};
constructor(initialState: LoanState) {
this.state = initialState;
}
public transition(event: string): void {
const nextState = this.transitions[this.state][event];
if (!nextState) {
throw new Error(`Transizione invalida: Impossibile eseguire ${event} dallo stato ${this.state}`);
}
console.log(`Transizione: ${this.state} -> ${nextState}`);
this.state = nextState;
this.onStateChange(nextState);
}
private onStateChange(newState: LoanState) {
// Hook per side effects (es. invio email, webhooks)
}
}
Persistenza e Database
A livello di database (SQL), la rappresentazione più efficiente non è una serie di booleani, ma una singola colonna status indicizzata, spesso supportata da un tipo ENUM per garantire l’integrità referenziale.
Tuttavia, per audit trail complessi (richiesti dalla normativa bancaria), è consigliabile una tabella separata loan_state_history:
loan_id(FK)previous_statenew_statetrigger_eventtimestampuser_id(chi ha scatenato la transizione)
Architettura Event-Driven e Side Effects
L’integrazione delle macchine a stati finiti con un’architettura a eventi è dove il CRM diventa proattivo. Ogni transizione di stato valida deve emettere un Domain Event.
Il Flusso Reattivo
- L’utente clicca “Approva Pratica” nella dashboard.
- L’API invoca la FSM:
fsm.transition('APPROVE'). - La FSM valida la logica, aggiorna il DB e, se successo, emette l’evento
LoanApprovedEventsu un message broker (es. RabbitMQ o Kafka). - Consumer disaccoppiati reagiscono:
- Il Notification Service invia un’email al broker.
- Il Document Service genera il PDF della delibera.
- Il Audit Service registra l’operazione per la compliance.
Questo disaccoppiamento impedisce che la logica di invio email “sporchi” la logica pura di approvazione del credito.
Prevenzione degli Stati Inconsistenti (Safety)
Nell’esperienza di sviluppo di sistemi come BOMA, abbiamo identificato tre regole d’oro per la sicurezza delle FSM:
- Atomicità: La transizione di stato e il salvataggio su DB devono avvenire nella stessa transazione database. Se il salvataggio fallisce, lo stato in memoria deve essere ripristinato.
- Idempotenza: Se un sistema esterno invia due volte l’evento
APPROVE, la FSM deve gestire il secondo tentativo con grazia (ignorandolo o restituendo lo stato attuale), senza creare duplicati o errori. - Guardie (Guards): Oltre alla validità della transizione (A -> B), è possibile implementare “guardie”. Esempio: “Puoi passare da ISTRUTTORIA a DELIBERA solo se la somma dei documenti caricati > 5”. Le guardie aggiungono un livello di logica condizionale controllata all’interno della struttura rigida della FSM.
Conclusione
L’adozione delle macchine a stati finiti nello sviluppo di CRM per mutui non è solo una scelta stilistica di codice, ma una decisione architetturale strategica. Essa sposta la complessità dalla gestione degli errori alla fase di progettazione, costringendo ingegneri e product manager a definire il processo di business con precisione chirurgica prima di scrivere una singola riga di codice. Il risultato è un sistema prevedibile, auditabile e, soprattutto, sicuro per la gestione di asset finanziari.
Domande frequenti

Una Macchina a Stati Finiti è un modello matematico che permette a un sistema di trovarsi in un unico stato definito in un dato momento, eliminando ambiguità operative. Nel contesto di un CRM per mutui, serve a gestire flussi complessi garantendo che le pratiche seguano percorsi deterministici e prevenendo stati inconsistenti tipici della gestione tramite semplici flag booleani.
L utilizzo di semplici flag booleani crea quella che viene definita spaghetti logic, generando un numero esponenziale di combinazioni di stati spesso impossibili da gestire e validare correttamente. Una FSM riduce l universo delle possibilità a un grafo diretto di stati validi e transizioni esplicite, rendendo il sistema più robusto, sicuro e facile da manutenere rispetto a una serie disordinata di condizioni condizionali.
Per implementare una FSM in stack moderni come Node.js o Python, è sconsigliato l uso di enormi strutture switch case, preferendo invece il State Pattern o librerie dedicate come XState. L approccio migliore prevede la definizione rigida di stati e transizioni, assicurando che ogni cambio di stato sia validato e possa scatenare eventi di dominio per integrare servizi esterni come notifiche o generazione documenti.
A livello di database SQL, la pratica più efficiente consiste nell utilizzare una singola colonna status indicizzata, spesso supportata da un tipo ENUM per garantire l integrità dei dati, invece di colonne booleane multiple. Per soddisfare i requisiti di conformità bancaria, è inoltre fondamentale affiancare una tabella di storico che registri ogni transizione, l evento scatenante, il timestamp e l utente responsabile dell azione.
Per garantire la sicurezza dei dati, è necessario rispettare regole come l atomicità, che assicura che transizione e salvataggio avvengano nella stessa transazione database, e l idempotenza, per gestire tentativi duplicati senza errori. Inoltre, l uso di guardie logiche permette di aggiungere condizioni specifiche, come la presenza di documenti minimi, prima di autorizzare il passaggio da uno stato all altro.




Hai trovato utile questo articolo? C'è un altro argomento che vorresti vedermi affrontare?
Scrivilo nei commenti qui sotto! Prendo ispirazione direttamente dai vostri suggerimenti.