f in x
Domain Driven Design: Bounded Context, Aggregate e Ubiquitous Language nella Pratica
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sviluppo di siti web

Domain Driven Design: Bounded Context, Aggregate e Ubiquitous Language nella Pratica

[2026-06-08] Author: Ing. Calogero Bono

Hai microservizi, ma i tuoi team litigano ancora su cosa sia un 'cliente'. Il database è condiviso, le API si parlano con modelli diversi, e il codice assomiglia a un colabrodo di concetti mescolati. Sei passato a un'architettura distribuita, ma la mente del dominio è rimasta monolitica. Questo è il problema che risolviamo qui.

Noi, di Meteora Web, lo vediamo ogni giorno nei progetti che ci arrivano da clienti che hanno già 'fatto il microservizio'. Hanno diviso il codice, ma non il ragionamento. Domain-Driven Design non è un lusso accademico: è lo strumento che trasforma la complessità in chiarezza, e i numeri lo dimostrano. Abbiamo gestito il sistema ERP di un negozio di abbigliamento dall'interno – stagioni, saldi, margini – e sappiamo quanto costa un linguaggio ambiguo. Un 'reso' per il magazzino non è lo stesso 'reso' per la contabilità. Se li mischi, perdi soldi.

In questa guida affrontiamo i tre pilastri operativi del DDD che servono davvero a chi costruisce microservizi: Ubiquitous Language, Bounded Context e Aggregate. Niente teoria inutilizzabile: esempi reali, codice che funziona, decisioni da prendere.

Ubiquitous Language: la lingua che unisce business e codice

Immagina un ristorante: il cameriere dice 'tavolo 4 ha ordinato un pollo alla griglia'. In cucina, lo stesso piatto è 'ospite 4, pollo, no glutine, senza osso'. Se cameriere e cuoco usassero due dizionari diversi, a ogni ordine ci sarebbero errori. Sostituisci 'cameriere' con il Product Owner e 'cuoco' con lo sviluppatore, e hai capito che cos'è Ubiquitous Language.

Nel nostro lavoro quotidiano, partiamo sempre da una sessione con il cliente per scrivere il glossario del dominio. Prendiamo un business process, ad esempio la vendita di un prodotto su un e-commerce, e diamo un nome univoco a ogni concetto: 'Ordine', 'Carrello', 'Cliente', 'Pagamento', 'Spedizione'. Poi usiamo quelle stesse parole nel codice: classi, metodi, tabelle, eventi. Se il cliente chiama 'ordine' ciò che in contabilità è 'transazione cliente', scegliamo una parola e la teniamo. Il linguaggio non è negoziabile una volta deciso.

Sponsored Protocol

Attenzione: errore comune — pensare che Ubiquitous Language sia solo un documento condiviso. Non lo è. È un impegno vivo: durante le riunioni, nei commenti del codice, nei nomi delle API. Se lo sviluppatore chiama una variabile $cliente e il dominio la chiama 'Acquirente', c'è una crepa. Noi risolviamo subito: rinomina o aggiorna il glossario, ma non convivere con l'ambiguità.

Cosa fare subito

Prendi un'ora con il tuo team (o con il cliente) e scrivi 5-10 termini del dominio che usate più spesso. Per ognuno: definizione univoca, esempi, sinonimi da evitare. Poi manda una mail a tutto il team: 'Da oggi, nel codice e nelle conversazioni, usiamo solo queste parole.' Il costo di un errore linguistico in produzione è sempre più alto di una riunione di allineamento.

Bounded Context: il confine che salva il progetto

Hai mai lavorato su un sistema dove la classe 'Prodotto' ha 50 campi, metà usati dal catalogo e metà dal magazzino? Quello è un modello onnivoro, sintomo di assenza di Bounded Context. In DDD, ogni contesto delimitato possiede il suo modello: nel contesto 'Catalogo', un prodotto ha nome, descrizione, prezzo, immagini. Nel contesto 'Magazzino', lo stesso prodotto ha quantità, ubicazione, lotto. Sono due concetti diversi, anche se parlano dello stesso oggetto fisico.

Sponsored Protocol

Noi abbiamo costruito una piattaforma proprietaria per la gestione social di più clienti. Abbiamo separato nettamente i contesti: 'Pubblicazione' (calendario, bozze, contenuti), 'Analytics' (metriche, report), 'Fatturazione' (clienti, prezzi, pagamenti). Ogni contesto ha il suo team (anche di una persona), il suo database, la sua logica. Il Bounded Context non è solo tecnico: è organizzativo. Se due team devono sincronizzarsi su uno stesso modello, il confine è sbagliato.

Come si identifica? Cerca le parole che cambiano significato. In un e-commerce di abbigliamento, 'taglia' per l'acquirente è una scelta estetica; per il magazzino è un codice di scaffale. Due contesti diversi. Traccia una Context Map: disegna i rettangoli dei contesti e le relazioni (Customer/Supplier, Partnership, Shared Kernel). Non serve uno strumento complesso – una lavagna o un diagramma su carta basta.

Esercizio operativo

Prendi il tuo progetto e individua almeno due concetti che vengono usati in modo diverso da due parti del sistema. Definisci per ciascuno un contesto delimitato. Poi verifica: i team responsabili parlano la stessa lingua? I database sono separati? Se no, hai trovato il primo candidato per la rifattorizzazione.

Aggregate: l'unità di consistenza

Finora abbiamo parlato di linguaggio e confini. Ma dentro un contesto, come gestiamo i dati che devono rimanere coerenti? Qui entra l'Aggregate: un cluster di oggetti del dominio che vengono trattati come un'unica unità per le modifiche. L'Aggregate Root è l'unico punto di accesso: ogni cambiamento passa attraverso di esso, che valida gli invarianti di business.

Sponsored Protocol

Esempio pratico: un ordine e le sue linee. Un 'Ordine' è l'Aggregate Root; 'RigaOrdine' è un'entità interna. Non si può aggiungere una riga a un ordine cancellato. Non si può modificare una riga senza che l'ordine lo sappia. L'Aggregate garantisce che le regole di business siano sempre rispettate.

Nel nostro lavoro, vediamo spesso il problema opposto: aggregati troppo grandi – un ordine con decine di righe, note, spedizioni, pagamenti, resi. Se ogni modifica carica tutto, la performance crolla. La regola d'oro: un aggregato dovrebbe essere abbastanza piccolo da essere modificato in una transazione, ma abbastanza grande da mantenere gli invarianti. Noi di Meteora Web usiamo un criterio semplice: se due entità possono essere modificate indipendentemente senza violare regole di business, sono due aggregati separati.

Ecco un esempio di codice in PHP (Laravel-like) di un Aggregate Root per un ordine:

class Order extends AggregateRoot
{
    private string $orderId;
    private string $status; // 'pending', 'confirmed', 'shipped'
    private array $items; // array of OrderItem
    private float $total;

    public function addItem(Product $product, int $quantity): void
    {
        if ($this->status !== 'pending') {
            throw new DomainException('Cannot add items to a non-pending order.');
        }
        $this->items[] = new OrderItem($product->getId(), $quantity, $product->getPrice());
        $this->recalculateTotal();
    }

    public function confirm(): void
    {
        if ($this->status !== 'pending') {
            throw new DomainException('Order already processed.');
        }
        if (count($this->items) === 0) {
            throw new DomainException('Cannot confirm an empty order.');
        }
        $this->status = 'confirmed';
        // Record event for other contexts to listen
        $this->recordEvent(new OrderConfirmed($this->orderId));
    }

    private function recalculateTotal(): void
    {
        $this->total = array_reduce($this->items, fn($carry, $item) => $carry + $item->getSubtotal(), 0);
    }
}

Nota: l'aggregato non esporta i suoi interni. La modifica avviene solo tramite metodi che esprimono l'intento di business. Se un servizio esterno vuole sapere il totale, chiamerà un metodo getter, ma non modifica mai direttamente le proprietà.

Sponsored Protocol

Quando usare due aggregati

Pensa al pagamento e all'ordine. Un pagamento può essere creato senza ordine? No. Ma può essere modificato (rimborso) senza che l'ordine cambi stato? Dipende. Se il rimborso non tocca gli invarianti dell'ordine (stato 'reso' se rimborsato parziale), allora sono due aggregati separati collegati da un ID. La regola del pollice: un aggregato per transazione.

Come DDD guida i microservizi

Ogni microservizio dovrebbe possedere un Bounded Context. Al suo interno, uno o più Aggregati. Le comunicazioni tra microservizi avvengono tramite eventi di dominio, non chiamate dirette che espongono l'interno. Possedere il proprio stack batte affittarlo: ogni contesto ha il suo database, il suo modello, la sua logica. I dati non sono mai condivisi a livello di tabella ma solo attraverso messaggi.

Esempio: quando un ordine viene confermato, l'Order service emette un evento OrderConfirmed. Il Payment service lo ascolta e crea un pagamento nel suo contesto. Il Billing service emette una fattura. Nessuno vede la tabella orders dell'altro. Questo è il vero disaccoppiamento.

Sponsored Protocol

Noi, di Meteora Web, abbiamo applicato questo pattern in una piattaforma proprietaria: ogni contesto (social, analytics, billing) pubblica eventi su un message broker (Redis Streams o RabbitMQ). I client non devono integrarsi con API sincrone a meno che non sia strettamente necessario. Il vantaggio economico? Meno downtime a cascata, scalabilità indipendente, e ogni team evolve il proprio modulo senza rompere gli altri.

Cosa fare adesso

Prendi un microservizio esistente e traccia i suoi confini di contesto. Identifica gli aggregati candidati. Per ognuno, scrivi gli invarianti: 'Un ordine non può essere spedito se non pagato' (esempio). Poi verifica se il tuo codice li rispetta. Se no, hai trovato il debito tecnico da saldare. Il DDD non va implementato tutto in un colpo: inizia da un contesto critico e poi espandi.

In sintesi — cosa fare

  • Costruisci un Ubiquitous Language con il business: glossario condiviso, usato in codice e riunioni.
  • Identifica i Bounded Context: dove i termini cambiano significato, traccia un confine netto.
  • Definisci gli Aggregati come unità di consistenza, con una radice che protegge gli invarianti.
  • Implementa comunicazione basata su eventi tra contesti, mai condivisione diretta di dati.
  • Rifattorizza gradualmente: non riscrivere tutto. Scegli un contesto, applica DDD, e verifica i risultati.

Se vuoi approfondire, leggi il libro di Eric Evans o l'articolo di Martin Fowler su DDD. E ricorda: un progetto senza DDD è come un negozio senza inventario: sembra bello, ma non sai mai cosa hai.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()