Parliamone
// tecnologie.database-design-modeling

Database Design & Modeling

Dalla modellazione relazionale e le forme normali al dimensional modeling e alla persistenza poliglotta: principi formali, trade-off architetturali e criteri di scelta per schemi dati in sistemi di produzione.

Data EngineeringSoftware Architecture

Executive summary

Ogni sistema informatico che gestisce informazioni strutturate dipende in modo critico da come queste informazioni vengono organizzate e collegate tra loro nel deposito permanente. Una progettazione inadeguata produce rigidità, errori nei dati e costi di manutenzione crescenti nel tempo, mentre una progettazione consapevole consente di adattare il sistema all'evoluzione delle esigenze senza riscritture radicali. Questo articolo analizza i principi fondamentali della progettazione degli schemi dati, dalle regole che eliminano le ridondanze alle strutture ottimizzate per l'analisi decisionale, fino alle strategie che combinano diverse tecnologie di archiviazione per rispondere a requisiti eterogenei. L'analisi evidenzia che non esiste un modello universalmente ottimale: la scelta dipende dal tipo di operazioni prevalenti, dai vincoli di prestazione e dalla complessità del dominio, e i sistemi più robusti adottano approcci differenziati per carichi di lavoro distinti.


Background

La modellazione dei dati è il processo di definizione della struttura logica attraverso cui un sistema rappresenta, vincola e manipola le informazioni persistenti. La qualità di questa modellazione influenza direttamente l'integrità dei dati, le prestazioni delle query, la manutenibilità del codice applicativo e la capacità del sistema di evolvere nel tempo. Un errore di progettazione nello schema dati è tra i debiti tecnici più costosi da correggere a posteriori, poiché le dipendenze si propagano dall'archiviazione fino ai livelli applicativi e analitici [1].

Il modello relazionale, formalizzato da Edgar F. Codd nel 1970, ha introdotto un fondamento matematico per la gestione dei dati basato sulla teoria degli insiemi e sulla logica dei predicati del primo ordine [2]. La proposta di Codd ha rappresentato una rottura radicale con i modelli navigazionali (gerarchico e reticolare) allora dominanti, in cui la struttura fisica dei dati determinava le modalità di accesso. Nel modello relazionale, i dati sono organizzati in relazioni (tabelle), e le operazioni sui dati sono espresse in modo dichiarativo attraverso un'algebra relazionale, separando la rappresentazione logica dall'implementazione fisica. Questa separazione ha reso possibile l'ottimizzazione automatica delle query e l'indipendenza dei programmi applicativi dalla struttura di archiviazione.

La teoria della normalizzazione, sviluppata dallo stesso Codd e successivamente raffinata da Boyce, Kent, Fagin e altri ricercatori nel corso degli anni '70 e '80, ha fornito criteri formali per eliminare le anomalie di aggiornamento, inserimento e cancellazione che emergono da schemi progettati in modo informale [2, 3]. Parallelamente, l'espansione dei sistemi informativi aziendali negli anni '90 ha evidenziato che gli schemi normalizzati, ottimali per i carichi transazionali, risultano inefficienti per le interrogazioni analitiche aggregate su grandi volumi di dati storici. Ralph Kimball e Bill Inmon hanno proposto approcci complementari al data warehousing che introducono strutture deliberatamente denormalizzate, star schema e snowflake schema, progettate per massimizzare le prestazioni analitiche [4, 5].

L'emergere di sistemi NoSQL nella seconda metà degli anni 2000, il paper su Bigtable di Google nel 2006, Dynamo di Amazon nel 2007, Apache Cassandra nel 2008, ha ulteriormente ampliato lo spettro dei modelli di dati disponibili, introducendo architetture document-oriented, column-family, key-value e a grafo che sacrificano parti delle garanzie ACID del modello relazionale in favore di scalabilità orizzontale, flessibilità di schema e tolleranza ai partizionamenti di rete [1, 6]. Questa diversificazione ha reso necessaria una disciplina progettuale capace di selezionare e combinare tecnologie di persistenza diverse in funzione dei requisiti specifici di ciascun carico di lavoro, un approccio noto come polyglot persistence [1].


Modellazione relazionale: fondamenti e algebra

Il modello di Codd e le sue proprietà

Il modello relazionale definisce i dati come relazioni matematiche: una relazione è un sottoinsieme del prodotto cartesiano di un insieme di domini, e ogni tupla rappresenta un fatto atomico del dominio applicativo [2]. Le proprietà fondamentali, assenza di duplicati, assenza di ordinamento tra le tuple, atomicità dei valori, derivano direttamente dalla definizione insiemistica e garantiscono che ogni relazione rappresenti un predicato logico verificabile. L'integrità del modello è assicurata da due vincoli strutturali: l'integrità di entità (ogni relazione possiede una chiave primaria non nulla che identifica univocamente ciascuna tupla) e l'integrità referenziale (ogni chiave esterna fa riferimento a una tupla esistente nella relazione referenziata, o è nulla).

L'algebra relazionale fornisce un insieme chiuso di operatori, selezione ($\sigma$), proiezione ($\pi$), prodotto cartesiano ($\times$), unione ($\cup$), differenza ($-$), ridenominazione ($\rho$), la cui composizione consente di esprimere qualsiasi interrogazione formulabile nel calcolo relazionale delle tuple [2, 7]. La chiusura dell'algebra, ovvero il fatto che ogni operazione su relazioni produca una relazione, è la proprietà che rende possibile la composizione arbitraria di query e l'ottimizzazione algebrica da parte del query planner. Le equivalenze algebriche, ad esempio il pushing down delle selezioni ($\sigma_{p}(R \bowtie S) \equiv \sigma_{p}(R) \bowtie S$ quando $p$ coinvolge solo attributi di $R$), costituiscono il fondamento su cui i moderni ottimizzatori basati su costo costruiscono i piani di esecuzione [7].

Dipendenze funzionali e chiavi

Il concetto di dipendenza funzionale è il mattone su cui si costruisce la teoria della normalizzazione. Data una relazione $R$, si dice che un insieme di attributi $X$ determina funzionalmente un attributo $Y$ (scritto $X \to Y$) se e solo se per ogni coppia di tuple $t_1, t_2 \in R$, $t_1[X] = t_2[X]$ implica $t_1[Y] = t_2[Y]$ [3]. Una chiave candidata è un insieme minimale di attributi che determina funzionalmente tutti gli altri attributi della relazione. L'identificazione delle dipendenze funzionali è un'attività di modellazione, non un'operazione automatica: richiede la comprensione delle regole del dominio applicativo e non può essere dedotta esclusivamente dai dati osservati, poiché un dataset specifico potrebbe soddisfare accidentalmente dipendenze che non valgono in generale.

Gli assiomi di Armstrong (riflessività, estensione, transitività) forniscono un sistema deduttivo sound e completo per derivare tutte le dipendenze funzionali implicate da un insieme dato [3]. La chiusura di un insieme di attributi $X^+$ rispetto a un insieme di dipendenze funzionali $F$, ovvero l'insieme di tutti gli attributi determinati funzionalmente da $X$ sotto $F$, è calcolabile in tempo polinomiale e costituisce lo strumento operativo per verificare se un attributo è determinato da un insieme di attributi candidato a chiave. Questo apparato formale, apparentemente astratto, ha implicazioni pratiche dirette: un errore nell'identificazione delle dipendenze funzionali si traduce in anomalie di aggiornamento che corrompono silenziosamente i dati nel tempo.


Normalizzazione: forme normali e trade-off

Prima, seconda e terza forma normale

La normalizzazione è il processo di decomposizione di una relazione in relazioni più piccole che preservano le dipendenze funzionali e consentono di ricostruire i dati originali senza perdita di informazione (decomposizione lossless-join) [3]. Ogni forma normale impone vincoli progressivamente più restrittivi sulla struttura delle dipendenze funzionali all'interno di una relazione.

La prima forma normale (1NF) richiede che tutti gli attributi contengano valori atomici, nessun attributo multivalore o composto [2]. Nella pratica contemporanea, questa definizione è stata rilassata dall'introduzione di tipi di dato strutturati: PostgreSQL supporta array e JSONB come tipi nativi, e lo standard SQL:2016 ha formalizzato il supporto per il tipo JSON [8]. La 1NF rimane comunque un principio progettuale valido nella misura in cui impone di esplicitare la struttura dei dati anziché nasconderla in campi opachi non interrogabili in modo relazionale.

La seconda forma normale (2NF) elimina le dipendenze parziali: ogni attributo non primo (non parte di alcuna chiave candidata) deve dipendere dall'intera chiave candidata, non da un suo sottoinsieme [3]. Le violazioni della 2NF si manifestano tipicamente in relazioni con chiavi composite dove un attributo dipende solo da una parte della chiave. La decomposizione in 2NF elimina le anomalie di aggiornamento associate: senza decomposizione, la modifica del valore di un attributo parzialmente dipendente richiede l'aggiornamento di tutte le tuple con lo stesso valore della porzione di chiave rilevante.

La terza forma normale (3NF) elimina le dipendenze transitive: un attributo non primo non deve dipendere dalla chiave candidata attraverso un altro attributo non primo [3]. Formalmente, una relazione è in 3NF se per ogni dipendenza funzionale non banale $X \to A$, almeno una delle seguenti condizioni è verificata: $X$ è una superchiave, oppure $A$ è un attributo primo. La 3NF è il livello di normalizzazione più frequentemente raggiunto nella pratica progettuale, poiché offre un equilibrio tra eliminazione delle anomalie e complessità dello schema risultante.

Forma normale di Boyce-Codd e oltre

La forma normale di Boyce-Codd (BCNF) impone un vincolo più stringente della 3NF: per ogni dipendenza funzionale non banale $X \to A$, $X$ deve essere una superchiave [3]. La differenza con la 3NF si manifesta solo in relazioni con chiavi candidate sovrapposte, un caso relativamente raro ma non trascurabile. Il prezzo della BCNF è la possibile perdita di preservazione delle dipendenze: esistono insiemi di dipendenze funzionali per i quali nessuna decomposizione in BCNF preserva tutte le dipendenze, mentre una decomposizione in 3NF che le preservi esiste sempre (algoritmo di sintesi) [3].

Le forme normali superiori, quarta forma normale (4NF, che elimina le dipendenze multivalore non banali) e quinta forma normale (5NF o Project-Join Normal Form, che elimina le dipendenze di join), affrontano classi di ridondanza che non coinvolgono dipendenze funzionali [3, 7]. La 4NF è rilevante nella pratica quando una relazione codifica due o più associazioni molti-a-molti indipendenti tra gli stessi insiemi di entità: la mancata decomposizione genera tuple spurie che rappresentano combinazioni non intenzionali. La 5NF, sebbene teoricamente completa, è raramente un obiettivo progettuale esplicito, dato che le violazioni della 5NF che non sono anche violazioni della 4NF sono estremamente rare nei domini applicativi reali.

Limiti della normalizzazione e quando fermarsi

La normalizzazione completa produce schemi con un elevato numero di relazioni e richiede operazioni di join per ricostruire le informazioni distribuite. In sistemi con carichi di lavoro prevalentemente di lettura su aggregazioni multi-tabella, il costo dei join può diventare un collo di bottiglia significativo. La decisione su quale forma normale adottare come obiettivo non è puramente teorica: dipende dal profilo del carico di lavoro (rapporto letture/scritture), dai requisiti di consistenza, dalle dimensioni attese dei dati e dalla capacità dell'infrastruttura di gestire join complessi [1].

Un principio progettuale ampiamente condiviso suggerisce di normalizzare almeno fino alla 3NF o BCNF come punto di partenza, per poi introdurre denormalizzazioni mirate e documentate dove i requisiti di prestazione lo giustificano [1, 7]. Questa strategia, normalize first, denormalize as needed, consente di mantenere un modello logico pulito come riferimento, derivando le strutture denormalizzate come viste materializzate o tabelle derivate che possono essere ricostruite dal modello normalizzato.


Denormalizzazione: strategie e criteri di applicazione

Motivazioni e rischi

La denormalizzazione è l'introduzione deliberata di ridondanza in uno schema al fine di migliorare le prestazioni di specifiche operazioni di lettura, riducendo il numero di join necessari per soddisfare le query più frequenti [1, 4]. A differenza di uno schema semplicemente non normalizzato (progettato male), uno schema denormalizzato è il risultato di una decisione architetturale consapevole, basata sull'analisi dei pattern di accesso e documentata in modo che le implicazioni sulla consistenza siano esplicite.

I rischi della denormalizzazione sono simmetrici ai benefici della normalizzazione. La ridondanza introdotta può generare inconsistenze se gli aggiornamenti non propagano correttamente le modifiche a tutte le copie del dato. Il costo di questa propagazione si manifesta in tre forme: aumento della complessità del codice applicativo (o dei trigger di database) che deve mantenere la consistenza, aumento del volume di scrittura su disco per ogni operazione di aggiornamento, e aumento del rischio di data corruption in caso di fallimenti parziali durante aggiornamenti multi-tabella [1]. La decisione di denormalizzare richiede pertanto un'analisi quantitativa: il guadagno in lettura deve essere sufficientemente significativo da giustificare il costo aggiuntivo in scrittura e manutenzione.

Pattern di denormalizzazione ricorrenti

Diversi pattern di denormalizzazione ricorrono nella pratica progettuale, ciascuno con un profilo di trade-off specifico. L'attributo calcolato (pre-computed column) consiste nel memorizzare il risultato di un calcolo frequente (un totale, una media, un conteggio) direttamente nella relazione, anziché ricalcolarlo ad ogni lettura. Questo pattern è particolarmente efficace quando il calcolo coinvolge aggregazioni su grandi volumi di dati e la frequenza di lettura supera di ordini di grandezza la frequenza di aggiornamento. PostgreSQL offre un meccanismo nativo per questo pattern attraverso le generated columns (stored), introdotte in PostgreSQL 12 in conformità con lo standard SQL:2003.

La duplicazione di attributi (column duplication) consiste nel copiare un attributo da una relazione referenziata nella relazione referenziante, eliminando la necessità di un join per accedervi. È il pattern più comune e anche il più rischioso: ogni modifica all'attributo nella relazione sorgente deve essere propagata a tutte le copie. Le viste materializzate rappresentano un approccio più robusto alla denormalizzazione, poiché mantengono la distinzione tra modello logico (normalizzato) e modello fisico (denormalizzato), con il DBMS responsabile del refresh della vista [8]. La scelta tra refresh sincrono e asincrono dipende dalla tolleranza dell'applicazione alla stale data: sistemi analitici tollerano tipicamente un refresh periodico (ogni minuto, ogni ora), mentre sistemi operativi con requisiti di consistenza forte richiedono un refresh immediato.

La tabella di sommario (summary table) è un caso specializzato di denormalizzazione in cui si mantiene una relazione separata contenente aggregazioni pre-calcolate a diversi livelli di granularità. Questo pattern anticipa il concetto di cube materializzato tipico del dimensional modeling e trova applicazione in dashboard operative, sistemi di monitoraggio e reporting in tempo quasi-reale.


Dimensional modeling per sistemi analitici

Il paradigma di Kimball e lo star schema

Il dimensional modeling è un approccio alla progettazione di schemi specificamente ottimizzato per carichi di lavoro analitici (OLAP), introdotto da Ralph Kimball nei primi anni '90 e formalizzato nel suo lavoro fondamentale [4]. L'intuizione centrale è che le query analitiche hanno una struttura prevedibile: misurano fatti quantitativi (ricavi, quantità, durate) segmentandoli per dimensioni categoriali (tempo, prodotto, cliente, geografia). Lo schema deve riflettere questa struttura per minimizzare la complessità delle query e massimizzare le prestazioni.

Lo star schema è la struttura portante del dimensional modeling. Al centro si colloca una fact table contenente le misure numeriche e le chiavi esterne verso le tabelle dimensionali, disposte a stella intorno ad essa [4]. Le fact table sono tipicamente strette (poche colonne di misure e chiavi esterne) ma profonde (milioni o miliardi di righe), mentre le dimension table sono larghe (molti attributi descrittivi) ma relativamente poco profonde. Ogni riga della fact table rappresenta un evento misurabile a un livello di granularità specifico, una transazione, una sessione, una spedizione, e la granularità determina sia la ricchezza analitica sia il volume del dato.

Le dimension table nello star schema sono volutamente denormalizzate: tutti gli attributi descrittivi di una dimensione sono collassati in una singola tabella piatta, anche quando relazioni gerarchiche esistono tra gli attributi (ad esempio, città → regione → nazione nella dimensione geografica) [4]. Questa denormalizzazione elimina i join tra tabelle dimensionali, semplifica le query e consente al query optimizer di applicare aggressivamente filtri (predicati) sulle dimensioni prima di accedere alla fact table, una tecnica nota come star join optimization implementata nei principali motori analitici.

Snowflake schema e varianti ibride

Lo snowflake schema normalizza parzialmente le dimension table, estraendo le gerarchie in tabelle separate [5]. Ad esempio, la dimensione prodotto viene decomposta in tabella prodotto, tabella categoria e tabella dipartimento, collegate da chiavi esterne. Questo approccio riduce la ridondanza nelle dimensioni e lo spazio di archiviazione, ma introduce join addizionali e complica le query. Nella pratica, lo snowflake schema è meno diffuso dello star schema puro per le analisi interattive, ma trova applicazione in data warehouse di grandi dimensioni dove il risparmio di spazio sulle dimensioni è significativo o dove le dimensioni sono condivise tra più fact table (conformed dimensions nel lessico di Kimball) [4].

Architetture ibride sono comuni nella pratica: le dimensioni con gerarchie profonde o con porzioni condivise tra più schemi stellari vengono parzialmente normalizzate, mentre le dimensioni semplici o a bassa cardinalità restano denormalizzate. La scelta è guidata dall'analisi dei pattern di query e dalla valutazione del trade-off tra complessità di manutenzione e prestazioni di lettura. Un criterio operativo utile è la frequenza di accesso alla gerarchia completa: se la maggior parte delle query filtra solo al livello più basso della gerarchia (ad esempio, il singolo prodotto), la normalizzazione della dimensione evita di replicare gli attributi dei livelli superiori senza penalizzare le query predominanti. Quando invece le query aggregano frequentemente ai livelli superiori (ad esempio, analisi per categoria o dipartimento), la denormalizzazione della dimensione riduce la latenza dell'operazione più comune.

Slowly Changing Dimensions e gestione della storicità

Un aspetto critico del dimensional modeling è la gestione delle modifiche agli attributi dimensionali nel tempo. Un cliente cambia indirizzo, un prodotto viene riclassificato, un dipendente cambia reparto: come deve reagire lo schema? Kimball ha classificato le strategie in tipi numerati (SCD Type 1, 2, 3 e oltre), ciascuno con implicazioni diverse sulla capacità di analisi storica [4].

Il Type 1 (overwrite) sovrascrive il valore precedente, perdendo la storia. Il Type 2 (add row) crea una nuova riga nella dimension table con colonne di validità temporale (effective_date, expiration_date, is_current), preservando la storia completa ma aumentando la cardinalità della dimensione e la complessità delle join condition. Il Type 3 (add column) aggiunge una colonna per il valore precedente (e.g., previous_region), consentendo un confronto limitato ma senza la complessità del Type 2. La scelta tra questi approcci dipende dai requisiti analitici: se l'analisi deve riflettere lo stato delle dimensioni al momento dell'evento misurato (analisi as-was), il Type 2 è necessario; se è sufficiente l'analisi sullo stato corrente (analisi as-is), il Type 1 è adeguato e significativamente più semplice.

Data Vault: un approccio alternativo per l'integrazione

Il Data Vault, proposto da Dan Linstedt e formalizzato nella sua seconda versione (Data Vault 2.0) [9], rappresenta un approccio alla modellazione del data warehouse che privilegia l'adattabilità e la tracciabilità rispetto alla semplicità di interrogazione. Lo schema è composto da tre tipi di entità: hub (chiavi di business con identificatori stabili), link (associazioni tra hub, che rappresentano relazioni e transazioni) e satellite (attributi descrittivi e contestuali, con versionamento temporale completo).

La separazione strutturale tra chiavi di business (hub), relazioni (link) e attributi (satellite) rende il Data Vault particolarmente resiliente ai cambiamenti del modello: l'aggiunta di una nuova fonte dati o di nuovi attributi non richiede la modifica delle strutture esistenti, ma solo l'aggiunta di nuovi satellite o link [9]. Il costo di questa flessibilità è la complessità delle query di lettura, che richiedono join multipli per ricostruire una vista integrata del dato. Per questa ragione, le architetture Data Vault prevedono tipicamente un livello di presentazione (business vault o information mart) che materializza le strutture denormalizzate necessarie per l'analisi.


Oltre il modello relazionale: modelli NoSQL e casi d'uso

Tassonomia dei modelli non relazionali

L'espansione dei sistemi distribuiti su larga scala ha evidenziato scenari in cui il modello relazionale impone vincoli architetturali incompatibili con i requisiti di scalabilità, disponibilità e flessibilità di schema. Il teorema CAP, formalizzato da Brewer e dimostrato da Gilbert e Lynch [10], stabilisce che un sistema distribuito non può garantire simultaneamente consistenza forte (ogni lettura restituisce il dato più recente), disponibilità (ogni richiesta riceve una risposta) e tolleranza al partizionamento (il sistema continua a funzionare nonostante la perdita di messaggi tra nodi). Questa impossibilità teorica ha motivato la progettazione di sistemi che rilassano esplicitamente una di queste proprietà in funzione dei requisiti applicativi.

I modelli NoSQL si articolano in quattro famiglie principali, ciascuna ottimizzata per un profilo di accesso specifico [1, 6]. I database key-value (Redis, Amazon DynamoDB) offrono latenze di accesso minimali per operazioni di lookup diretto, a fronte di capacità di interrogazione limitate alla chiave. I database document-oriented (MongoDB, Couchbase) memorizzano documenti semi-strutturati (tipicamente JSON/BSON) che consentono schema flessibili e query su campi interni, con il documento come unità atomica di consistenza. I database column-family (Apache Cassandra, HBase) organizzano i dati in famiglie di colonne con ampia capacità di distribuzione orizzontale, eccellendo per carichi di lavoro con pattern di accesso prevedibili e volumi nell'ordine dei petabyte. I database a grafo (Neo4j, Amazon Neptune) rappresentano nativamente entità e relazioni, consentendo traversal efficienti su strutture fortemente connesse dove i join relazionali diventerebbero proibitivamente costosi [6].

Modellazione query-driven nei sistemi NoSQL

Un principio fondamentale della modellazione per sistemi NoSQL è l'inversione della logica progettuale rispetto al modello relazionale. Nella modellazione relazionale, lo schema è derivato dalla struttura del dominio (entità e relazioni) e le query si adattano allo schema. Nella modellazione NoSQL, lo schema è derivato dai pattern di accesso: si parte dalle query che il sistema deve soddisfare e si progetta la struttura dati che le serve nel modo più efficiente [6, 11].

In Apache Cassandra, ad esempio, il design della tabella è guidato dalla query: ogni tabella è progettata per rispondere a una specifica query, con la partition key che determina la distribuzione dei dati sui nodi e la clustering key che determina l'ordinamento all'interno della partizione [11]. Questo approccio implica che lo stesso dato può essere replicato in tabelle diverse con chiavi di partizionamento diverse per servire query differenti, una denormalizzazione deliberata che sacrifica lo spazio di archiviazione e la complessità di aggiornamento in favore di letture predicibili con latenza costante. La modellazione efficace in Cassandra richiede pertanto una conoscenza approfondita dei pattern di accesso prima della progettazione dello schema, un requisito che inverte la sequenza tradizionale (schema → applicazione) in (applicazione → schema).

Nel caso dei database a grafo, la modellazione segue principi propri. I nodi rappresentano entità, gli archi rappresentano relazioni tipizzate e direzionali, e le proprietà annotano sia nodi sia archi [6]. L'efficienza dei database a grafo nell'esecuzione di traversal multi-hop, dove ogni hop richiede un join nel modello relazionale, li rende particolarmente adatti per domini come reti sociali, fraud detection, gestione delle dipendenze e knowledge graph. La scelta tra modello relazionale e modello a grafo dipende dalla profondità media dei traversal necessari: per query che coinvolgono 1-2 join, il modello relazionale è tipicamente competitivo o superiore; oltre i 3-4 livelli di profondità, il vantaggio prestazionale del modello a grafo diventa significativo [6].


Polyglot persistence: architetture multi-modello

Il principio della persistenza differenziata

Il concetto di polyglot persistence, introdotto da Martin Fowler e reso sistematico da Sadalage e Fowler [6], propone che un'applicazione complessa utilizzi tecnologie di persistenza diverse per sottosistemi diversi, selezionate in funzione dei requisiti specifici di ciascun carico di lavoro. Un sistema di e-commerce potrebbe utilizzare un database relazionale per il catalogo prodotti e la gestione degli ordini (dove l'integrità transazionale è critica), un database document-oriented per i profili utente e le preferenze (dove la flessibilità di schema è prioritaria), un database a grafo per il motore di raccomandazione (dove i traversal relazionali sono frequenti) e un database key-value per la gestione delle sessioni e della cache (dove la latenza minimale è il requisito dominante).

Questo approccio riconosce esplicitamente che nessun singolo modello di dati è ottimale per tutti i carichi di lavoro [1, 6]. La selezione della tecnologia di persistenza diventa pertanto una decisione architetturale di primo livello, guidata da criteri quali il profilo di lettura/scrittura, i requisiti di consistenza, i pattern di scalabilità attesi, la complessità delle relazioni tra i dati e le competenze del team di sviluppo.

Sfide architetturali della polyglot persistence

L'adozione della polyglot persistence introduce complessità architetturale significativa che deve essere gestita consapevolmente. La consistenza cross-store è la sfida primaria: quando un'operazione di business coinvolge dati distribuiti su tecnologie di persistenza diverse, il mantenimento della consistenza richiede meccanismi di coordinamento espliciti. Il pattern Saga, formalizzato da Garcia-Molina e Salem [12] e ampiamente adottato nelle architetture a microservizi, gestisce le transazioni distribuite attraverso una sequenza di transazioni locali compensabili, rinunciando all'atomicità globale in favore della consistenza eventuale. Ogni passo della saga viene eseguito come una transazione locale nel rispettivo data store, e in caso di fallimento vengono eseguite transazioni compensative in ordine inverso per annullare gli effetti dei passi precedenti.

La complessità operativa rappresenta un costo non trascurabile. Ogni tecnologia di persistenza richiede competenze specialistiche per configurazione, monitoraggio, backup, recovery e capacity planning. Il total cost of ownership di un'architettura con cinque tecnologie di persistenza diverse non è la somma dei costi individuali: include il costo di integrazione, la complessità della pipeline di deploy, la frammentazione delle competenze nel team e il rischio di failure mode inattesi nelle interazioni tra sistemi [1].

Il pattern database-per-service, tipico delle architetture a microservizi, porta la polyglot persistence alla sua espressione più estrema: ogni servizio è proprietario esclusivo del proprio data store e nessun altro servizio può accedere direttamente ai dati di un servizio diverso [13]. Questo isolamento massimizza l'indipendenza dei team e la libertà di scelta tecnologica, ma impone che ogni interrogazione cross-service avvenga attraverso API, con implicazioni su latenza, consistenza e complessità delle query aggregate.

NewSQL e database multi-modello: convergenze

L'evoluzione recente del panorama dei database mostra una tendenza alla convergenza. I sistemi NewSQL (CockroachDB, YugabyteDB, TiDB) combinano le garanzie ACID e l'interfaccia SQL del modello relazionale con la scalabilità orizzontale tipica dei sistemi NoSQL, utilizzando protocolli di consenso distribuito (Raft, Paxos) per la replicazione e il coordinamento [14]. Questi sistemi rendono meno netta la dicotomia tra relazionale e NoSQL, offrendo un'alternativa per carichi di lavoro che richiedono sia transazionalità sia distribuzione geografica.

Parallelamente, i database multi-modello (ArangoDB, Azure Cosmos DB, SurrealDB) supportano nativamente più modelli di dati (documenti, grafi, key-value) all'interno di un singolo motore, riducendo la complessità operativa della polyglot persistence al costo di un possibile compromesso sulle prestazioni rispetto ai sistemi specializzati per ciascun modello [1]. La valutazione di questi sistemi richiede un'analisi empirica specifica per il carico di lavoro target: le prestazioni dichiarate per uno specifico modello di dati in un database multi-modello possono divergere significativamente da quelle di un sistema nativo per quel modello, specialmente sotto carico elevato o per query complesse.


Limiti, problemi aperti e implicazioni pratiche

Schema evolution e migrazione

Uno dei problemi più sottovalutati nella pratica della modellazione dei dati è la gestione dell'evoluzione dello schema nel tempo. Ogni sistema in produzione subisce modifiche allo schema, aggiunta di colonne, modifica di tipi, ristrutturazione di relazioni, e la capacità di eseguire queste modifiche senza downtime o corruzione dei dati è un requisito operativo critico [1]. I database relazionali tradizionali impongono modifiche di schema bloccanti (ALTER TABLE acquisisce lock esclusivi); soluzioni come pt-online-schema-change per MySQL e pg_repack per PostgreSQL mitigano questo problema ma introducono complessità operativa. I database document-oriented adottano un approccio diverso, tollerando la coesistenza di documenti con strutture diverse nella stessa collezione (schema-on-read), ma spostano la complessità della validazione a livello applicativo.

Lo standard emergente prevede l'uso di strumenti di schema migration versionata (Flyway, Liquibase, Atlas) che trattano le modifiche dello schema come artefatti versionati nel sistema di controllo di versione, applicabili in modo idempotente e reversibile [15]. Questo approccio, noto come evolutionary database design, allinea l'evoluzione dello schema ai cicli di rilascio del software e consente la gestione di ambienti multipli (sviluppo, staging, produzione) con la stessa disciplina applicata al codice applicativo.

Il ruolo del modellatore umano nell'era dell'automazione

Nonostante i progressi negli strumenti di automazione, generazione di schema da modelli ER, reverse engineering di schemi esistenti, suggerimenti basati su machine learning per l'ottimizzazione degli indici, la modellazione dei dati rimane un'attività intrinsecamente interpretativa che richiede la comprensione del dominio applicativo [7]. L'identificazione delle dipendenze funzionali, la scelta della granularità della fact table, la decisione su quali denormalizzazioni introdurre e la selezione delle tecnologie di persistenza in un'architettura poliglotta sono tutte decisioni che richiedono giudizio ingegneristico informato dalla conoscenza del contesto di business, dei pattern di accesso previsti e dei vincoli operativi.

Un errore ricorrente nella pratica è la replicazione acritica di pattern architetturali adottati da organizzazioni con scala e complessità radicalmente diverse. L'architettura polyglot di un sistema che serve miliardi di richieste giornaliere con centinaia di ingegneri dedicati non è un modello appropriato per un sistema con migliaia di utenti e un team di cinque persone. La complessità architetturale ha un costo operativo che cresce in modo non lineare con il numero di tecnologie adottate, e la scelta più efficace per molte organizzazioni resta un database relazionale ben progettato, eventualmente affiancato da un singolo sistema complementare per un carico di lavoro specifico [1].

Direzioni future

La crescente adozione di architetture event-driven e di pattern come event sourcing e CQRS (Command Query Responsibility Segregation) sta ridefinendo il rapporto tra modello di scrittura e modello di lettura [13]. In un sistema basato su event sourcing, lo stato corrente è derivato dalla sequenza ordinata di eventi immutabili, e il modello di lettura è una proiezione materializzata di tali eventi in una struttura ottimizzata per le query del consumatore. Questo approccio consente di mantenere proiezioni multiple, relazionali, dimensionali, denormalizzate, degli stessi eventi, ciascuna aggiornata in modo asincrono, eliminando la necessità di scegliere un unico schema che soddisfi carichi di lavoro eterogenei. Il costo è la complessità della gestione della consistenza eventuale tra le proiezioni e la necessità di meccanismi robusti di replay e ricostruzione.

L'integrazione di capacità vettoriali nei database relazionali rappresenta una seconda tendenza rilevante. Estensioni come pgvector per PostgreSQL consentono di memorizzare embedding e di eseguire ricerche per similarità semantica (approximate nearest neighbor) direttamente nel database transazionale, senza introdurre un vector store specializzato nell'architettura [8]. Questa convergenza riduce la complessità operativa della polyglot persistence per applicazioni che richiedono sia interrogazioni relazionali tradizionali sia ricerche semantiche, un caso sempre più frequente con la diffusione di sistemi che integrano modelli di linguaggio. Il trade-off risiede nelle prestazioni: i database relazionali con estensioni vettoriali non raggiungono la throughput dei sistemi nativi per carichi puramente vettoriali, ma offrono un compromesso accettabile per volumi moderati.

L'adozione di formati colonnari aperti (Apache Parquet, Apache Iceberg, Delta Lake) come strato di archiviazione condiviso tra motori di query diversi prospetta un'evoluzione verso architetture data lakehouse che separano lo strato di archiviazione dallo strato di elaborazione [16]. In queste architetture, i dati risiedono in un object store (S3, GCS, Azure Blob) in formato aperto, e motori diversi (Spark, Trino, DuckDB, Snowflake) accedono agli stessi dati senza duplicazione. Apache Iceberg, in particolare, introduce garanzie transazionali (snapshot isolation, schema evolution, time travel) su dati in formato colonnare, colmando parte del divario storico tra data lake e data warehouse in termini di affidabilità e governabilità dei dati.


Riferimenti

[1] M. Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017.

[2] E. F. Codd, "A Relational Model of Data for Large Shared Data Banks," Communications of the ACM, vol. 13, no. 6, pp. 377-387, 1970. https://doi.org/10.1145/362384.362685

[3] R. Ramakrishnan, J. Gehrke, Database Management Systems, 3rd ed., McGraw-Hill, 2003.

[4] R. Kimball, M. Ross, The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling, 3rd ed., Wiley, 2013.

[5] W. H. Inmon, Building the Data Warehouse, 4th ed., Wiley, 2005.

[6] P. J. Sadalage, M. Fowler, NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence, Addison-Wesley, 2013.

[7] H. Garcia-Molina, J. D. Ullman, J. Widom, Database Systems: The Complete Book, 2nd ed., Pearson, 2009.

[8] PostgreSQL Global Development Group, "PostgreSQL Documentation," 2025. https://www.postgresql.org/docs/

[9] D. Linstedt, M. Olschimke, Building a Scalable Data Warehouse with Data Vault 2.0, Morgan Kaufmann, 2016.

[10] S. Gilbert, N. Lynch, "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services," ACM SIGACT News, vol. 33, no. 2, pp. 51-59, 2002. https://doi.org/10.1145/564585.564601

[11] Apache Software Foundation, "Apache Cassandra Documentation," 2025. https://cassandra.apache.org/doc/latest/

[12] H. Garcia-Molina, K. Salem, "Sagas," in Proc. ACM SIGMOD, 1987. https://doi.org/10.1145/38713.38742

[13] S. Newman, Building Microservices, 2nd ed., O'Reilly Media, 2021.

[14] CockroachDB, "CockroachDB Architecture Documentation," 2025. https://www.cockroachlabs.com/docs/stable/architecture/overview.html

[15] P. Ambler, P. Sadalage, Refactoring Databases: Evolutionary Database Design, Addison-Wesley, 2006.

[16] Apache Software Foundation, "Apache Iceberg Documentation," 2025. https://iceberg.apache.org/docs/latest/

Database Design & Modeling

Raccontaci la situazione. Rispondiamo entro 24 ore nei giorni lavorativi.

Tweaks

Light mode
Atmospheric (glass)
Client logos
Terminal hero