
Il problema
Il cliente doveva individuare compatibilità tra migliaia di elementi, con parametri multipli (tipo: questo componente funziona con quello solo se...). Query tradizionali su database relazionali? Troppo lente, join che esplodono a causa del volume dei dati.
Per dare un'idea concreta: un utente selezionava un componente e il sistema doveva restituire tutti gli elementi compatibili, filtrati per tipo, versione e una serie di vincoli incrociati. Con il database relazionale, la query attraversava cinque o sei tabelle in join. Su un dataset piccolo, funzionava. Sul catalogo reale, con decine di migliaia di elementi e milioni di combinazioni possibili, la query non tornava. O meglio: tornava, ma dopo tempi che nessun utente avrebbe mai aspettato.
La soluzione temporanea era stata una serie di viste materializzate, aggiornate di notte. Il risultato: dati di compatibilità sempre in ritardo di 24 ore, una tabella intermedia che cresceva senza controllo, e un processo batch che ogni tanto falliva in silenzio. Il classico workaround che diventa architettura (e genera debito tecnico).
La scelta del grafo
Ho proposto un database a grafo, integrato con il sistema esistente, per trattare relazioni native senza forzature.
La domanda ovvia: perché un grafo e non una denormalizzazione aggressiva, o ElasticSearch, o una cache? La risposta è nel tipo di query. Quando cerchi compatibilità, stai attraversando relazioni. Non stai filtrando un dataset piatto. Stai chiedendo: "da questo nodo, quali altri nodi raggiungo, passando per quali vincoli?" È esattamente il tipo di domanda per cui i database a grafo sono progettati. Un RDBMS lo fa con join ricorsive. Un grafo lo fa con un traversal.
Ho valutato Neo4j e ArangoDB. Neo4j è lo standard de facto, ma ArangoDB offriva un vantaggio nel contesto: è multi-model (documenti + grafi), il che significava poter gestire sia i dati di catalogo che le relazioni in un unico sistema. E per un progetto dove l'RDBMS rimaneva la fonte di verità, avere un layer intermedio più flessibile aveva senso. In più, ArangoDB è open-source senza limitazioni di feature nella versione community.
L'RDBMS rimane come unica fonte di verità ma le relazioni finiscono tutte nel grafo.
Cosa ho fatto
- Analizzato i dataset esistenti.
- Progettato il modello a grafo.
- Generato dati rappresentativi (pseudocasuali ma realistici).
- ETL custom per importare tutto.
- Query ottimizzate per compatibilità.
- Interfaccia semplice per visualizzare risultati.
L'ETL è stato il pezzo più delicato. I dati nel database relazionale non erano pensati per essere letti come un grafo: le relazioni di compatibilità erano implicite, sparse tra colonne di tabelle diverse, a volte codificate in campi di testo libero. Mappare tutto in nodi e archi ha richiesto un lavoro di analisi che è stato, di fatto, un audit del dato.
E quell'audit ha tirato fuori di tutto. Voci duplicate con codici diversi che si riferivano allo stesso componente fisico. Regole di compatibilità contraddittorie: il componente A risultava compatibile con B in una tabella, ma incompatibile in un'altra (inserita probabilmente anni dopo, da un'altra persona, senza verificare). Record orfani che puntavano a prodotti rimossi dal catalogo da tempo, mai cancellati perché nessuna query li toccava più. Campi "note" che contenevano vincoli reali, scritti in linguaggio naturale, mai formalizzati in una regola strutturata.
Rendere esplicite queste relazioni ha avuto un effetto collaterale prezioso: il cliente ha potuto correggere dati che generavano errori a valle da anni. Compatibilità sbagliate che finivano nei preventivi, componenti suggeriti che in realtà non funzionavano insieme. L'audit del dato, alla fine, si è rivelato quasi altrettanto prezioso del grafo stesso.
Dataset di test: oltre 40.000 elementi, più di 252 milioni di relazioni.
Risultati
Prima del grafo, le query di compatibilità più complesse andavano in timeout o richiedevano le viste materializzate (dati vecchi di 24 ore). Dopo:
- Query complesse sotto gli 800 ms, su hardware limitato e senza ottimizzazioni particolari.
- Sistema flessibile: facile aggiungere nuovi componenti o regole.
- Scalabile senza riscrivere il legacy.
Per dare contesto: "hardware limitato" significa una VM condivisa con altri servizi, 8 GB di RAM e 4 vCPU. Non un server dedicato, non un cluster. Il minimo sindacale per far girare ArangoDB con un dataset di quella dimensione. Con un cluster dedicato (anche solo tre nodi con 32 GB ciascuno e storage SSD) i tempi scenderebbero in modo significativo, probabilmente sotto i 200 ms per le stesse query.
Il confronto con la situazione precedente è netto. Le query SQL equivalenti, quelle con cinque o sei join e filtri incrociati, impiegavano dai 15 ai 45 secondi sul database relazionale. Le più pesanti andavano direttamente in timeout dopo 60 secondi. Per questo erano state introdotte le viste materializzate: non come ottimizzazione, ma come unica via per ottenere una risposta. Il prezzo era avere dati vecchi di un giorno e un processo batch fragile.
Con il grafo, le stesse domande di compatibilità tornano in 800 ms. In tempo reale, su dati aggiornati, senza batch notturni. Il punto non era la performance assoluta: era passare da "inutilizzabile" a "interattivo." E 800 ms è interattivo.
Stack
- Python per script e backend.
- Flask per API RESTful.
- ArangoDB come database a grafo (open-source, performante nei traversing).
- Docker per containerizzare e deploy dell'ambiente.
La scelta di Python e Flask era pragmatica: il team del cliente già lavorava in Python. Introdurre un grafo era già un cambio abbastanza grande. Aggiungere anche un linguaggio nuovo sarebbe stato troppo. L'obiettivo era risolvere il problema di compatibilità, non riscrivere lo stack.
Quando un grafo non serve
Non ogni relazione giustifica un database a grafo. Se le tue query sono semplici lookup (dato un ID, dammi i dettagli), un RDBMS va benissimo. Se hai bisogno di ricerca full-text, ElasticSearch è la scelta ovvia. Il grafo entra in gioco quando le relazioni sono il dato, non un effetto collaterale delle join.
Un esempio concreto: un altro cliente stava valutando un database a grafo per il proprio catalogo prodotti. Le categorie erano organizzate ad albero (elettronica > componenti > resistenze > SMD) e serviva navigare la gerarchia per mostrare breadcrumb e filtri. Sembra un caso da grafo, ma non lo è. Le relazioni erano semplici parent-child, una struttura ad albero puro. Una recursive CTE in PostgreSQL risolveva il problema in poche righe di SQL, con performance eccellenti. Introdurre un database a grafo avrebbe significato un nuovo servizio da gestire, monitorare e aggiornare, sincronizzazione dati tra due sistemi, competenze aggiuntive nel team. Complessità operativa in cambio di zero beneficio reale. Gli ho consigliato di restare su PostgreSQL e investire il budget altrove.
Il segnale più chiaro che il grafo serve davvero: se ti trovi a scrivere query con tre o più livelli di join, e ogni livello aggiunge condizioni, stai probabilmente modellando un grafo dentro un sistema che non lo è.
Per NDA non entro in dettagli sensibili o codice specifico, ma se hai un catalogo prodotti, supply chain o relazioni complesse che rallentano tutto...
Questo caso l'ho documentato in modo più strutturato come Architecture Decision Record, con contesto reale, trade-off valutati e risultati osservati.
ADR241015: Uso di un database a grafo per gestire compatibilità complesse in un catalogo prodotti enterprise →Parliamone. Ti dico se un grafo è la strada giusta o se basta ottimizzare quello che hai.

In sintesi (TL;DR)
- Le query di compatibilità tra migliaia di prodotti con vincoli incrociati facevano esplodere le join SQL: timeout o dati vecchi di 24 ore.
- ArangoDB (multi-model, open-source) come layer a grafo sopra l'RDBMS esistente, che resta fonte di verità.
- Query complesse sotto gli 800 ms su 252 milioni di relazioni, su hardware non dedicato.
- Il grafo serve quando le relazioni sono il dato, non un effetto collaterale delle join. Se bastano lookup semplici, l'RDBMS va benissimo.