Skip to main content
backend30 novembre 202513 min di lettura

Scegliere il Database Giusto: Relazionale vs NoSQL per Progetti Reali

Un framework decisionale pratico per scegliere tra PostgreSQL, MongoDB, Firebase, Redis e altri database in base alle tue reali esigenze.

databasepostgresqlmongodb
Scegliere il Database Giusto: Relazionale vs NoSQL per Progetti Reali

"Dipende" è la risposta onesta a ogni domanda sui database, ma è anche inutile. Quando stai avviando un progetto e devi scegliere un database, hai bisogno di qualcosa di più concreto di un elenco di compromessi. Hai bisogno di un framework decisionale che tenga conto delle tue reali esigenze — forma dei dati, pattern di query, esperienza del team, aspettative di scalabilità e complessità operativa.

Ho utilizzato PostgreSQL, MongoDB, Firebase/Firestore, Redis e SQLite in diversi progetti di produzione. Ogni scelta era giusta per il suo contesto, e alcune erano sbagliate e hanno dovuto essere migrate. I pattern dietro quelle decisioni sono più utili di qualsiasi confronto di benchmark.

Il Framework Decisionale

Prima di esaminare i singoli database, rispondi a queste cinque domande sul tuo progetto:

1. Qual è la forma dei tuoi dati?

Non si tratta di "relazionale vs documento". Si tratta di come le tue entità di dati si relazionano tra loro.

Altamente relazionale: Gli utenti hanno ordini, gli ordini hanno articoli, gli articoli appartengono a categorie, le categorie hanno gerarchie. Se disegni il tuo modello di dati e assomiglia a un grafo con molte connessioni, hai bisogno di forti capacità relazionali.

Orientato ai documenti: Ogni entità è relativamente autonoma. Un post di blog contiene il suo titolo, corpo, tag e informazioni sull'autore. Raramente hai bisogno di fare join tra tipi di entità.

Chiave-valore: Devi archiviare e recuperare per chiave. Dati di sessione, configurazione, feature flags.

Serie temporali: Eventi, metriche, log. Molte scritture, solo aggiunte, interrogati per intervallo di tempo.

2. Quali sono i tuoi pattern di query?

Query note: Sai esattamente quali query eseguirà l'applicazione. E-commerce: "ottieni gli ordini dell'utente", "trova prodotti per categoria", "calcola il ricavo totale di questo mese". Le query note favoriscono i database relazionali dove puoi ottimizzare con indici e piani di query.

Query ad-hoc: Gli utenti possono cercare, filtrare e aggregare in modi imprevedibili. Dashboard analitiche, funzionalità di ricerca, strumenti di reporting. Questi favoriscono motori di query flessibili.

Ricerca semplice: La maggior parte delle letture sono "ottieni documento per ID". I database a documenti e i key-value store eccellono qui.

3. Qual è il tuo requisito di consistenza?

Consistenza forte: Banche, inventario, qualsiasi cosa in cui la lettura di dati obsoleti causa problemi reali. I database relazionali con transazioni ACID sono la scelta sicura.

Consistenza eventuale: Feed sociali, analisi, caching. Puoi tollerare la lettura di dati leggermente obsoleti in cambio di prestazioni e disponibilità.

4. Qual è la tua traiettoria di scalabilità?

La scalabilità verticale va bene: La maggior parte delle applicazioni. Se il tuo database si adatta a una singola macchina con spazio per crescere, qualsiasi database funziona. PostgreSQL su un server moderno gestisce milioni di righe senza problemi.

La scalabilità orizzontale è richiesta: Devi distribuire i dati su più macchine. Questo è più raro di quanto la maggior parte degli sviluppatori pensi, ma quando ne hai bisogno, la tua scelta di database è limitata.

5. Cosa sa il tuo team?

Questo fattore è sottovalutato. Un team di esperti PostgreSQL sarà più produttivo con PostgreSQL, anche se MongoDB è teoricamente più adatto al modello di dati. Il costo di apprendimento di un nuovo database — il debug di errori sconosciuti, l'apprendimento delle migliori pratiche operative, la comprensione delle caratteristiche di performance — è reale e significativo.

PostgreSQL: La Scelta Predefinita

Se non sei sicuro, scegli PostgreSQL. Questa non è un'opinione controversa tra gli ingegneri backend — è il consenso. PostgreSQL gestisce dati relazionali, documenti JSON, ricerca full-text, query geospaziali e dati di serie temporali. Non è il migliore in nessuna di queste aree, ma è abbastanza buono in tutte da servire come singolo database per la maggior parte delle applicazioni.

Punti di Forza

Transazioni ACID. Quando devi aggiornare più tabelle atomicamente — dedurre l'inventario e creare un ordine, trasferire denaro tra conti — PostgreSQL garantisce la correttezza.

Ricche capacità di query. Funzioni finestra, CTE (Common Table Expressions), query ricorsive, join laterali. SQL non è solo SELECT * FROM — è un potente linguaggio di query che può esprimere analisi complesse senza spostare i dati a un livello applicativo.

-- Find each user's most recent order with running total
WITH ranked_orders AS (
  SELECT
    user_id,
    order_id,
    total,
    created_at,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as rn,
    SUM(total) OVER (PARTITION BY user_id ORDER BY created_at) as running_total
  FROM orders
)
SELECT * FROM ranked_orders WHERE rn = 1;

Colonne JSONB. Quando parte dei tuoi dati è genuinamente schemaless — preferenze utente, risposte a moduli dinamici, payload di webhook di terze parti — puoi archiviarli come JSONB e interrogarli con pieno supporto all'indicizzazione.

-- Store and query semi-structured data
CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  attributes JSONB DEFAULT '{}'
);

-- Index a specific JSON path
CREATE INDEX idx_products_color ON products USING GIN ((attributes->'color'));

-- Query JSON data
SELECT name FROM products
WHERE attributes->>'color' = 'blue'
AND (attributes->>'weight')::numeric < 10;

Ecosistema di estensioni. PostGIS per dati geospaziali, pg_trgm per la ricerca testuale fuzzy, TimescaleDB per serie temporali, pgvector per gli embedding AI. Il modello di estensioni significa che PostgreSQL può adattarsi a carichi di lavoro specializzati senza sostituire il tuo database primario.

Quando PostgreSQL Non È la Scelta Giusta

  • Elevato throughput di scrittura con scalabilità orizzontale. PostgreSQL scala bene verticalmente (macchina più grande) ma lo sharding orizzontale è complesso. Se hai bisogno di scrivere milioni di eventi al secondo su decine di macchine, considera soluzioni appositamente costruite.
  • Prototipazione rapida dove i cambiamenti di schema avvengono quotidianamente. Nelle prime fasi di una startup, quando il modello di dati cambia fondamentalmente ogni settimana, il sovraccarico delle migrazioni può rallentarti rispetto alle opzioni schemaless.
  • Quando il tuo team non ha esperienza SQL e la timeline del progetto non consente l'apprendimento. Questo è raro, ma succede.

MongoDB: Quando i Documenti Hanno Senso

MongoDB riceve più critiche di quante ne meriti. Il marketing iniziale ("schemaless! web scale!") ha creato aspettative irrealistiche, e molti sviluppatori lo hanno usato per casi d'uso in cui PostgreSQL sarebbe stato migliore. Ma ci sono scenari genuini in cui MongoDB è la scelta giusta.

Quando MongoDB Vince

Sistemi di gestione dei contenuti. Ogni pezzo di contenuto ha una struttura diversa — gli articoli hanno campi diversi dai video, che hanno campi diversi dai podcast. In MongoDB, questi sono tutti documenti nella stessa collezione con forme diverse. In PostgreSQL, o crei una tabella ampia con molte colonne nullable, usi colonne JSON (a quel punto stai usando il paradigma di MongoDB in PostgreSQL), o crei una tabella per tipo con join complessi.

Event sourcing e logging. Elevato throughput di scrittura con documenti che sono principalmente scritti e letti, raramente aggiornati e mai uniti.

I documenti incorporati riducono i join. Se un ordine ha sempre bisogno dei suoi articoli, e gli articoli non vengono mai acceduti senza il loro ordine, incorporare gli articoli all'interno del documento dell'ordine significa che la tua query più comune è una singola ricerca di documento invece di un join tra due tabelle.

// MongoDB document with embedded sub-documents
{
  _id: ObjectId("..."),
  customer: {
    name: "John Doe",
    email: "john@example.com"
  },
  items: [
    { product: "Widget", quantity: 2, price: 9.99 },
    { product: "Gadget", quantity: 1, price: 24.99 }
  ],
  total: 44.97,
  status: "shipped",
  createdAt: ISODate("2026-03-01T10:00:00Z")
}

La scalabilità orizzontale è una funzionalità di prima classe. Lo sharding di MongoDB è integrato e ben documentato. Se hai veramente bisogno di distribuire dati su molti nodi (centinaia di milioni di documenti con alto throughput), MongoDB gestisce questo in modo più elegante di PostgreSQL.

Quando MongoDB È Sbagliato

  • Dati altamente relazionali. Se stai scrivendo aggregazioni $lookup (la versione di MongoDB dei join) in ogni query, hai scelto il database sbagliato.
  • Quando hai bisogno frequentemente di transazioni tra collezioni. MongoDB supporta transazioni multi-documento, ma aggiungono latenza e complessità. Se le tue operazioni principali richiedono atomicità tra collezioni, il modello di transazione di PostgreSQL è più naturale.
  • Quando hai effettivamente bisogno di uno schema. "Schemaless" sembra liberatorio finché non ti rendi conto che ogni pezzo di codice che legge dal database è implicitamente uno schema. Senza uno schema imposto dal database, sposti la validazione al livello dell'applicazione, dove è più facile sbagliare e più difficile applicare in modo coerente.

Firebase e Firestore: Sviluppo Rapido

Firebase Realtime Database e Firestore servono una nicchia specifica: applicazioni in cui la velocità di sviluppo conta più della purezza del modello di dati, e dove il backend è principalmente un livello di persistenza e sincronizzazione dei dati piuttosto che un motore di logica di business complesso.

Quando Firebase Brilla

App mobile-first con sincronizzazione in tempo reale. Gli SDK di Firebase gestiscono caching offline, listener in tempo reale e risoluzione dei conflitti out-of-the-box. Costruire questo da zero con PostgreSQL richiede un livello WebSocket, una strategia di caching e un codice personalizzato significativo.

Piccoli team senza ingegneri backend. Firebase elimina la necessità di gestire un server di database, costruire un livello API, implementare l'autenticazione e gestire l'archiviazione dei file. Per un team di due persone che costruisce un MVP, questa riduzione del sovraccarico operativo può fare la differenza tra rilasciare e non rilasciare.

Prototipazione e validazione. Quando hai bisogno di testare un'idea con utenti reali in due settimane, Firebase ti permette di concentrarti interamente sull'applicazione client. Se l'idea si valida, puoi migrare a un backend più tradizionale in seguito.

Limitazioni di Firebase

Vincoli di query in Firestore. Non puoi interrogare campi che non sono indicizzati. Non puoi fare filtri di disuguaglianza su più campi. Non puoi fare ricerca full-text. Queste limitazioni ti costringono a denormalizzare aggressivamente e talvolta a duplicare i dati tra le collezioni.

// Firestore: You CAN'T do this
db.collection('products')
  .where('price', '>', 10)
  .where('rating', '>', 4)
  .orderBy('name')  // Error: needs composite index on price + rating + name

// Firestore: You CAN do this (with a composite index)
db.collection('products')
  .where('price', '>', 10)
  .where('rating', '>', 4)
  .orderBy('price')  // Must order by a field used in inequality

Vendor lock-in. Il tuo modello di dati, le regole di sicurezza e i pattern di query sono tutti specifici di Firebase. Migrare da Firebase è una riscrittura, non una migrazione.

Imprevedibilità dei costi. Firebase fattura per lettura/scrittura di documenti. Una query mal ottimizzata o un listener in tempo reale su una grande collezione può generare fatture sorprendenti. Ho visto progetti in cui un singolo listener mal configurato è costato più di quanto avrebbe fatto un server PostgreSQL dedicato per un anno.

Logica lato server limitata. Le Cloud Functions possono gestire alcune elaborazioni lato server, ma la logica di business complessa — transazioni multi-step, aggregazione di dati, elaborazione in background — richiede comunque soluzioni creative o un backend separato.

Decisione: Firebase vs Backend Tradizionale

Fattore Firebase PostgreSQL + API
Tempo per MVP Giorni Settimane
Sovraccarico operativo Quasi nullo Moderato
Flessibilità query Limitata SQL completo
Costo su scala Imprevedibile Prevedibile
Vendor lock-in Alto Basso
Supporto offline Integrato Da costruire
Logica di business complessa Difficile Naturale
Team richiesto Solo Frontend Frontend + Backend

Redis: Caching, Code e Altro

Redis non è un database primario per la maggior parte delle applicazioni (anche se Redis con moduli di persistenza può svolgere quel ruolo). È un archivio di strutture dati ad alte prestazioni che eccelle in problemi specifici.

Caching

Il caso d'uso più comune di Redis. Metti in cache query di database costose, risposte API o risultati calcolati.

import redis
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def get_user_profile(user_id: str):
    # Check cache first
    cached = r.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)

    # Cache miss — query database
    profile = db.query("SELECT * FROM users WHERE id = %s", user_id)

    # Cache for 5 minutes
    r.setex(f"user:{user_id}", 300, json.dumps(profile))
    return profile

Archiviazione Sessioni

Il modello chiave-valore di Redis con supporto TTL (time-to-live) è una soluzione naturale per i dati di sessione. È più veloce delle sessioni basate su database e più semplice delle soluzioni basate su JWT per applicazioni stateful.

Rate Limiting

def is_rate_limited(user_id: str, limit: int = 100, window: int = 60) -> bool:
    key = f"rate:{user_id}"
    current = r.incr(key)
    if current == 1:
        r.expire(key, window)
    return current > limit

Code di Lavoro (Job Queues)

Le liste e gli stream di Redis sono eccellenti code di lavoro. Librerie come Bull (Node.js), Celery (Python) e Sidekiq (Ruby) usano Redis come loro message broker.

Quando Non Usare Redis

  • Come unico database. Redis è in-memory per impostazione predefinita. Anche con la persistenza (snapshot RDB o log AOF), non è progettato per dati che non puoi permetterti di perdere.
  • Per query complesse. Le strutture dati di Redis (stringhe, liste, set, sorted set, hash) sono potenti ma non interrogabili come un database. Accedi ai dati per chiave, non per condizioni arbitrarie.
  • Quando non hai un problema di caching. Aggiungere Redis a uno stack che non necessita di caching aggiunge complessità operativa senza alcun beneficio. Una query PostgreSQL che impiega 5ms non ha bisogno di essere messa in cache.

Utilizzo di Database Multipli

La maggior parte delle applicazioni di produzione di moderata complessità utilizza più di un database. La chiave è usare ciascuno per ciò che fa meglio, non cercare di forzare un database a gestire tutto.

Un pattern comune per un'applicazione web:

  • PostgreSQL per i dati di business principali (utenti, ordini, prodotti).
  • Redis per caching, sessioni e rate limiting.
  • S3 (o equivalente) per l'archiviazione di file (immagini, documenti, backup).

Per un'applicazione in tempo reale:

  • PostgreSQL per dati persistenti e query complesse.
  • Redis per pub/sub e caching.
  • Firebase/Supabase per la sincronizzazione in tempo reale con i client mobili.

Il Pattern di Integrazione

Quando si utilizzano più database, stabilisci una chiara proprietà. Ogni pezzo di dati ha una fonte autorevole, e altri sistemi sono cache o viste di quei dati.

PostgreSQL (source of truth)
    ├── Redis (read cache, expires after TTL)
    ├── Elasticsearch (search index, synced via change data capture)
    └── Firebase (mobile sync, updated via webhooks)

Non avere mai due database che si considerano entrambi proprietari degli stessi dati. Quel percorso porta a incubi di consistenza quasi impossibili da debuggare.

Considerazioni sulla Migrazione

Se ti rendi conto di aver scelto il database sbagliato, la migrazione è possibile ma costosa. Ecco le considerazioni pratiche:

La migrazione da MongoDB a PostgreSQL è la più comune che ho visto. La ragione usuale è che l'applicazione è cresciuta oltre le query sui documenti e necessitava di join complessi, transazioni o aggregazioni. La migrazione comporta la progettazione di uno schema relazionale, la scrittura di script di trasformazione e l'aggiornamento di ogni query di database nell'applicazione. Prevedi da due a quattro settimane per un'applicazione moderatamente complessa.

La migrazione da PostgreSQL a MongoDB è più rara ma si verifica quando il modello di dati di un'applicazione diventa prevalentemente orientato ai documenti. La migrazione è meccanicamente più semplice (appiattire le tabelle in documenti) ma richiede di ripensare ogni query e di perdere le garanzie di transazione.

La migrazione da Firebase a PostgreSQL è la più difficile perché non è solo un cambiamento di database — è un cambiamento di architettura. Devi costruire un livello API, implementare l'autenticazione, sostituire i listener in tempo reale con WebSockets o polling e gestire la sincronizzazione offline. Questo è più vicino a una riscrittura che a una migrazione.

La migliore migrazione è quella che eviti. Dedica un giorno in più a pensare al tuo modello di dati in anticipo. Parla con qualcuno che ha costruito un'applicazione simile. Il costo di scegliere il database giusto inizialmente è sempre inferiore al costo di migrare in seguito.

Analisi dei Costi

I costi dei database su scala possono essere sorprendenti, specialmente con i servizi gestiti.

Servizio Livello Gratuito Piccola Produzione Media Produzione
Supabase (PostgreSQL) 500MB, 2 progetti ~$25/mese (8GB, 2 core) ~$100/mese (32GB, 4 core)
Neon (PostgreSQL) 0.5GB storage ~$19/mese ~$69/mese
MongoDB Atlas 512MB condivisi ~$57/mese (M10 dedicato) ~$200/mese (M30)
Firebase Firestore 1GB storage, 50k letture/giorno ~$25-100/mese (varia enormemente) $100-1000/mese (dipendente dalle query)
Redis Cloud 30MB ~$7/mese (250MB) ~$60/mese (1GB)
PlanetScale (MySQL) 5GB, 1B letture righe/mese ~$39/mese ~$99/mese

L'imprevedibilità dei costi di Firebase merita enfasi. Ho visto progetti in cui i costi sono rimasti sotto i $30/mese per mesi, per poi salire a $300 dopo il lancio di una funzionalità che ha aumentato le letture dei documenti. Con PostgreSQL o MongoDB, i costi correlano con la dimensione della macchina, il che è prevedibile.

Il Mio Processo Decisionale Reale

Quando inizio un nuovo progetto, la decisione di solito procede così:

  1. Predefinito PostgreSQL a meno che non ci sia una ragione specifica per non farlo.
  2. Aggiungi Redis se l'applicazione ha esigenze di caching, rate limiting o code di lavoro.
  3. Considera Firebase/Supabase se l'applicazione è mobile-first con requisiti in tempo reale e il team è piccolo.
  4. Considera MongoDB se il modello di dati è genuinamente orientato ai documenti con requisiti relazionali minimi.
  5. Aggiungi database specializzati (Elasticsearch, TimescaleDB, ecc.) solo quando un carico di lavoro specifico lo richiede.

Questo framework mi è stato utile in una serie di progetti, dalle piattaforme di gestione di ristoranti alle applicazioni mobili per la salute. L'intuizione chiave è che il database è infrastruttura — dovrebbe servire le esigenze della tua applicazione, non guidare la tua architettura. Scegli l'opzione "noiosa" che funziona, e dedica i tuoi sforzi ingegneristici alle parti dell'applicazione che ti differenziano.

DU

Danil Ulmashev

Full Stack Developer

Interessato a collaborare?