DDD non inizia da una cartella `domain`
Eric Evans presenta DDD come un insieme di pattern e definizioni per lavorare con modelli di dominio, mentre Martin Fowler sottolinea la modellazione di domini complessi e lo sviluppo di un linguaggio condiviso tra sviluppatori ed esperti di business. [1][2][4]
Di conseguenza, una struttura `domain/application/infrastructure` non rende automaticamente un'applicazione DDD. Si possono avere cartelle perfettamente nominate e lavorare comunque solo con DTO e logica procedurale. Il DDD strategico può inoltre essere applicato senza una struttura canonica a livelli.
DDD strategico e DDD tattico operano a livelli diversi
Il DDD strategico riguarda la suddivisione di un dominio ampio in sottodomini e Bounded Contexts e le relazioni tra essi. Microsoft descrive l'analisi di dominio come identificazione di sottodomini e contesti, mentre Fowler considera Bounded Context un pattern centrale del DDD strategico. [3][7]
Il DDD tattico lavora all'interno di un contesto specifico e include pattern come Entities, Value Objects, Aggregates e Domain Services. Microsoft separa esplicitamente analisi strategica e modellazione tattica. [8]
Nel frontend conviene spesso iniziare dal livello strategico e decidere solo dopo quali contesti necessitano davvero di un modello tattico ricco.
Un Bounded Context non è un livello tecnico
Fowler descrive un Bounded Context come un confine entro cui un modello rimane coerente e i termini hanno significato non ambiguo. Lo stesso concetto, come `Customer` o `Product`, può avere significati diversi in contesti differenti. [3]
Per questo `frontend`, `backend`, `React app`, `route` o `micro-frontend` non sono automaticamente Bounded Contexts. I confini derivano dal modello e dal linguaggio del dominio, non dalla tecnologia. Anche guide pratiche sul DDD frontend sottolineano questa distinzione. [15][16]
Quando DDD nel frontend ha senso
| Segnale | Direzione | Perché |
|---|---|---|
| Molte regole di business variabili | DDD | Un modello di dominio evita regole sparse |
| Gli stessi concetti hanno significati diversi nel prodotto | DDD | I Bounded Contexts mantengono più modelli coerenti |
| Più team ed esperti di dominio collaborano | DDD | Ubiquitous Language riduce l'ambiguità |
| CRUD semplice e form | Modularità più semplice | Il costo del DDD tattico può superare il beneficio |
| Il frontend rispecchia soprattutto un'API | Modularità più semplice | C'è poca logica di dominio propria del client |
| Applicazione piccola con un dominio coerente | Modularità più semplice | Confini e livelli aggiuntivi possono portare poco valore |
Il segnale più forte è la presenza di regole di business complesse e mutevoli lato client: configuratori, processi multi-step, pricing, permessi, transizioni di stato, workflow o modelli diversi dello stesso concetto in aree differenti del prodotto.
DDD è utile anche quando più team lavorano su un grande prodotto e le stesse parole assumono significati diversi in aree differenti. I Bounded Contexts servono proprio a gestire più modelli coerenti. [3]
DDD Crew raccomanda prima di comprendere il dominio, identificare i sottodomini strategicamente importanti, definire le responsabilità dei Bounded Contexts e solo dopo codificare il modello. [10]
Quando DDD diventa sovra-architettura
Se il frontend principalmente recupera dati, li mostra, gestisce form e invia semplici comandi CRUD senza regole di business rilevanti lato client, l'intero insieme dei pattern tattici DDD raramente risolve un problema concreto.
Microsoft afferma esplicitamente che per un contesto CRUD semplice un modello dati anemico può essere sufficiente e pattern DDD più complessi possono non valere il costo. Un modello ricco è più utile quando esistono molte regole di business in continua evoluzione. [9]
Lo stesso criterio dovrebbe valere nel frontend: la sofisticazione dell'architettura deve seguire la complessità del dominio, non l'ambizione tecnica del team.
Ubiquitous Language dovrebbe essere visibile anche nel codice UI
Ubiquitous Language è un linguaggio comune e rigoroso costruito da sviluppatori ed esperti di dominio intorno al modello. Fowler sottolinea che deve evolvere insieme alla comprensione del dominio. [4]
Nel frontend, nomi di use case, azioni, tipi, moduli, schermate e stati dovrebbero quindi usare il vocabolario di business quando esprimono comportamento di dominio.
Se il business parla di `approveApplication` ma il codice usa `setFlag2` o `handleData`, il codice smette di essere una rappresentazione chiara del linguaggio del dominio.
Organizza un frontend grande per domini, non solo per tipi di file
Fowler osserva che una struttura top-level `view/model/data` può essere adeguata nei sistemi piccoli, ma crescendo è spesso meglio avere moduli top-level orientati al dominio che contengono internamente i propri livelli. [5]
La documentazione attuale di Nx segue una direzione simile: le cartelle possono diventare confini di ownership di dominio e le librerie interne possono essere classificate come `feature`, `ui`, `data-access` e `util`. [12]
Una struttura pratica può quindi usare `libs/orders/...`, `libs/billing/...`, `libs/identity/...` invece di un unico albero globale `components/`, `services/`, `models/`.
Separa comportamento di presentazione e comportamento di dominio
Fowler descrive la separazione tra presentazione, logica di dominio e accesso ai dati come una modularizzazione efficace. Osserva anche che il codice UI è spesso più difficile da testare, quindi mantenere la logica di dominio fuori dalla presentazione migliora la testabilità. [5][6]
Un componente frontend dovrebbe principalmente renderizzare, gestire eventi UI e delegare operazioni. Regole come se un ordine possa essere annullato, come calcolare uno sconto o quale transizione di stato sia valida dovrebbero avere una sede esplicita fuori da JSX, template o classi di componente.
Questo non vieta la logica di presentazione nei componenti. Validazione della vista, visibilità, focus locale o animazioni sono diverse dalle regole di business.
Livelli pratici dentro un contesto frontend
| Livello | Responsabilità | Esempi |
|---|---|---|
| presentation / ui | Rendering e comportamento dell'interfaccia | components, routes, view state |
| application | Coordinamento dei casi d'uso | commands, use cases, orchestration |
| domain | Regole, concetti e invarianti di dominio | entities, value objects, policies |
| infrastructure / data-access | Integrazioni tecniche | HTTP, storage, SDKs, mappers |
DDD non impone una struttura di cartelle obbligatoria. Separare `presentation`, `application`, `domain` e `infrastructure` è un'interpretazione pratica della separazione delle responsabilità, non un requisito formale di Evans. [1][5]
Il livello domain può contenere regole, concetti e comportamenti indipendenti dal framework; application coordina i use case; infrastructure adatta HTTP, storage e SDK esterni; presentation contiene componenti, routing e stato puramente UI.
Modello API e modello di dominio frontend non devono essere identici
Un Bounded Context può avere il proprio modello di un concetto e contesti diversi possono mappare rappresentazioni differenti. Fowler usa `Customer` e `Product` come esempi comuni di termini con significati diversi a seconda del contesto. [3]
Un DTO backend non deve quindi arrivare direttamente in ogni componente. Un mapper o adapter può trasformare il contratto di trasporto nel modello usato da uno specifico contesto frontend.
Questo confine è particolarmente utile quando un'API serve più client, è legacy, combina più domini o evolve indipendentemente. Per un endpoint CRUD semplice, un ulteriore livello di mapping può essere superfluo.
Entities, Value Objects e Aggregates: usali in modo selettivo
DDD distingue concetti come Entities, Value Objects, Services e Aggregates. Fowler li evidenzia come parte del vocabolario di Evans, mentre Microsoft descrive gli aggregati come pattern tattico per mantenere la coerenza del modello. [2][8]
Il frontend non dovrebbe copiare il modello backend uno a uno solo per avere le stesse classi. Un Value Object può essere utile per `Money`, `DateRange` o `Email` se protegge invarianti reali. Un Aggregate ha senso se il client deve davvero garantire regole di coerenza del modello.
Se un tipo è solo dati per renderizzare una tabella, un semplice tipo TypeScript può essere migliore di Entity, Factory, Repository e Service.
Stato UI e stato di dominio non sono la stessa cosa
React tratta l'organizzazione dello stato come un problema di progettazione e raccomanda di evitare stato ridondante o duplicato. È una buona base tecnica, ma reducer, store o signals non creano da soli un modello di dominio. [14]
Stato come `isModalOpen`, tab attiva o posizione di scroll appartiene alla presentazione. Il ciclo di vita di un ordine, le transizioni consentite o le regole di un configuratore possono appartenere al dominio.
Separare le due categorie evita store globali che mescolano dati server, comportamento di business e dettagli UI.
I confini vanno applicati, non solo documentati
Nx permette di definire tag di progetto e vincoli dichiarativi sulle dipendenze, per esempio bloccando import tra scope o tipi di libreria. `@nx/enforce-module-boundaries` può verificare gli import durante il lint. [11]
Nx supporta anche più dimensioni di tag, permettendo di modellare separatamente `scope`, tipo di libreria, stabilità o distinzione client/server. [13]
Le decisioni DDD possono quindi diventare regole CI: `billing` non importa gli interni di `identity`, `ui` non dipende da `data-access` e `domain` rimane indipendente dal framework.
Frontend e backend possono condividere il dominio senza condividere un modello identico
Un Bounded Context è un confine di modello e linguaggio e non dovrebbe quindi essere tracciato automaticamente sulla separazione di rete tra browser e server. Guide pratiche sul DDD frontend sottolineano che i contesti possono attraversare livelli tecnici ed essere implementati congiuntamente da frontend e backend. [15][16]
La rappresentazione client può comunque differire da quella server perché interazione, stato locale e presentazione hanno esigenze diverse. È importante preservare la semantica e mappare esplicitamente, non copiare classi.
Un micro-frontend non dovrebbe essere creato solo perché esiste un Bounded Context. Confine di dominio e confine di deployment sono decisioni separate.
La testabilità è un forte argomento per isolare la logica di dominio
Fowler indica la testabilità come beneficio della separazione tra presentazione e dominio. La logica fuori dalla UI può essere testata senza renderizzare componenti o dipendere da dettagli dell'interfaccia. [5][6]
Nel frontend questo consente test rapidi di regole, Value Objects, use case e transizioni di stato come normale TypeScript o JavaScript. I test dei componenti restano necessari, ma non devono essere l'unico luogo in cui verificare le regole di business.
Un segnale di allarme è quando ogni modifica a una regola richiede di montare un componente completo e mockare router, store, HTTP e API del browser.
Introdurre DDD in un frontend esistente senza un grande rewrite
DDD Crew raccomanda un processo iterativo: comprendere il dominio, identificare sottodomini importanti, definire le responsabilità dei Bounded Contexts e poi codificare il modello. [10]
In un frontend esistente è più sicuro scegliere un'area di business problematica, definirne linguaggio e confine, separare presentazione e regole, definire l'API pubblica del modulo e solo dopo migrare altre funzionalità.
Non serve migrare tutta l'applicazione. DDD può essere applicato dove la complessità di business ripaga il costo di modellazione, lasciando le aree semplici come moduli funzionali più leggeri.
- Parti da un problema di business, non dalle cartelle.
- Identifica termini, regole e concetti ambigui.
- Definisci un Bounded Context e il suo contratto pubblico.
- Separa regole di dominio, componenti e DTO di trasporto.
- Aggiungi solo pattern tattici che risolvono una complessità concreta.
- Trasforma i confini in regole di lint o CI.
- Misura dipendenze, cicli, regressioni e costo dei cambiamenti trasversali.
- Non migrare aree CRUD semplici solo per simmetria architetturale.

