Architecture Decision Records - ADR241015
2024/10/15Uso di un database a grafo per gestire compatibilità complesse in un catalogo prodotti enterprise
Autore: Marco Orlandin, Architect
Data: 15 Ottobre 2024 (aggiornato Dicembre 2025)
Status: Implementato con successo
Settore: Manufacturing / Cataloghi tecnici
Dimensione dataset: >40.000 elementi, >252 milioni di relazioni
Constraint principali: Performance (query <1 secondo), integrazione con legacy RDBMS, costi contenuti (open-source preferred), rispetto NDA cliente
Contesto e problema
In un progetto per un cliente enterprise, dovevamo gestire la compatibilità tra migliaia di componenti tecnici (immagina un catalogo prodotti con varianti, accessori, versioni software/firmware, norme di sicurezza, ecc.).
Un componente A è compatibile con B solo se soddisfa una serie di condizioni multiple (tipo di connessione, versione, certificazione regionale, ecc.). Il sistema legacy usava un database relazionale classico (PostgreSQL), con tabelle normalizzate e join multiple per verificare le compatibilità.
Problema principale:
Le query diventavano esplosive. Con volumi reali, una singola verifica di compatibilità richiedeva secondi o decine di secondi – inaccettabile per un’interfaccia utente o un configuratore online. Le join cartesiane crescevano esponenzialmente con l’aggiunta di nuovi parametri.
Obiettivo: portare le query complesse sotto il secondo, senza riscrivere tutto il legacy e mantenendo l’RDBMS come source of truth.
Requisiti non funzionali
- Tempo di risposta query < 800 ms (95° percentile)
- Scalabilità orizzontale futura
- Integrazione bidirezionale con RDBMS esistente
- Costi operativi bassi (no licenze proprietarie pesanti)
- Facilità di evoluzione del modello (nuovi tipi di relazione)
Opzioni valutate
Opzione 1: Ottimizzare l'RDBMS esistente
- Indicizzazioni avanzate, materialized views, denormalizzazione parziale.
- Pro: Zero nuove tecnologie, minimo impatto sul legacy.
- Contro: Limiti strutturali – le relazioni many-to-many complesse restano costose. Difficile scalare oltre un certo punto senza riscritture massive.
Opzione 2: Graph database dedicato (ArangoDB / Neo4j)
- Rappresentare nodi (componenti) e archi (relazioni di compatibilità) in modo nativo.
- Pro: Traversing di grafo estremamente veloci, modello dati naturale per relazioni.
- Contro: Introduzione nuova tecnologia, necessità di ETL/sincronizzazione, curva di apprendimento team.
Opzione 3: Soluzioni NoSQL document-oriented (es. MongoDB con aggregation)
- Pro: Flessibile, scalabile.
- Contro: Le query di traversing profondo restano complesse e lente rispetto a un graph DB nativo.
Decisione
Ho scelto ArangoDB (open-source, multi-model) come database a grafo dedicato, integrato con il sistema esistente.
Motivazioni principali:
- Performance nativa sui traversing (ordini di grandezza migliore di SQL join).
- Licenza open-source + possibilità di deploy su infrastructure esistente.
- Supporto AQL (query language simile a SQL) → curva apprendimento bassa per il team.
- Facilità di integrazione via ETL periodico/custom.
L’RDBMS resta la singola fonte di verità per i dati master; il grafo viene popolato via ETL e usato solo per query di compatibilità.
Implementazione passo-passo
- Analisi dataset: Mappato entità e relazioni esistenti.
- Modello grafo: Nodi = Componenti (con proprietà), Archi = COMPATIBLE_WITH (con proprietà condizionali).
- Dati di test: Generati pseudocasuali ma realistici (>40k nodi, >252M archi).
- ETL custom: Script Python per estrarre, trasformare e caricare (usa ArangoDB driver).
- API: Flask per esporre endpoint RESTful di query compatibilità.
- Deploy: Tutto containerizzato con Docker per riproducibilità.
- Monitoring: Query profiling integrato in ArangoDB.
Conseguenze osservate (dopo go-live)
- Performance: Query complesse passate da secondi/decine di secondi a <800 ms su hardware modesto, senza tuning avanzato.
- Flessibilità: Aggiungere nuovi tipi di compatibilità = solo nuovi tipi di archi, senza alter table.
- Manutenibilità: Team ha apprezzato la leggibilità del modello grafo.
- Rischi mitigati: Sincronizzazione ETL schedulata + fallback su RDBMS in caso di discrepanza.
- Lezioni apprese:
- Inizia con un PoC su subset dati reali.
- Monitora la densità del grafo (troppi archi → ottimizza con filtri preliminari).
- Non tutto deve andare nel grafo: usa ibrido quando serve.
Stack utilizzato
- Database grafo: ArangoDB (open-source)
- Backend/API: Python + Flask
- ETL: Script Python custom
- Containerizzazione: Docker
- Legacy: PostgreSQL (come source of truth)
Quando considerare un database a grafo
Se hai:
- Relazioni many-to-many profondamente annidate
- Query di traversing ricorrenti (compatibilità, raccomandazioni, bill of materials)
- Evoluzione frequente del modello relazionale
...e le prestazioni SQL non bastano più, un grafo è spesso la soluzione più pulita.
Se invece le relazioni sono semplici o il volume è basso, ottimizzare l’RDBMS è quasi sempre sufficiente.
Hai un caso simile (catalogo prodotti, supply chain, configuratori)?
Contattami. Valutiamo insieme se un grafo risolve o se basta un tuning mirato.