Modellare il dominio
Al cuore di qualsiasi applicazione guidata dal dominio cβΓ¨ il modello che rappresenta la conoscenza centrale e le regole di business del sistema. Modellare bene il dominio significa tradurre i concetti del mondo reale in strutture software espressive, coese e consistenti.
EntitΓ
Un'entitΓ Γ¨ un oggetto definito principalmente dalla sua identitΓ e non solo dai suoi attributi. Anche se gli attributi cambiano nel tempo, l'identitΓ di un'entitΓ rimane la stessa.
Caratteristiche principali:
- Possiede un'identitΓ unica (di solito un
Id). - CiΓ² che conta Γ¨ chi Γ¨ l'entitΓ , non solo cosa contiene.
- I suoi attributi possono cambiare nel tempo.
Creare un'entitΓ con Lino
Per creare una nuova entitΓ usando Lino, esegui:
lino entity new
Il wizard CLI ti chiederΓ :
- Servizio β Il servizio in cui l'entitΓ sarΓ creata.
- Modulo β Il modulo in cui sarΓ creata l'entitΓ (solo per servizi modulari).
- Nome dell'entitΓ β Il nome usato nel dominio e nella tabella del database.
Successivamente, definirai i campi che compongono l'entitΓ configurando ciascuno di essi.
Tipi di campi disponibili
| Tipo | Descrizione | Intervallo / Note |
|---|---|---|
short | Intero a 16 bit | -32.768 β 32.767 |
int | Intero a 32 bit | -2.147.483.648 β 2.147.483.647 |
long | Intero a 64 bit | -9.223.372.036.854.775.808 β 9.223.372.036.854.775.807 |
string | Testo | Fino a circa 2 miliardi di caratteri |
bool | Valore booleano | true o false |
Guid | Identificatore globale univoco | UnicitΓ distribuita |
decimal | Numero decimale ad alta precisione | Ideale per valori monetari |
float | Punto flottante (32 bit) | β 6β9 cifre di precisione |
double | Punto flottante (64 bit) | β 15β17 cifre di precisione |
DateTime | Data e ora | Include il fuso orario |
DateOnly | Solo data (C# 10+) | β |
TimeOnly | Solo ora (C# 10+) | β |
Entity | Riferimento a un'altra entitΓ | 1:1 o 1:N |
Value Object | Value Object immutabile | Es.: Indirizzo, CPF |
Enum | Enumerazione | Insieme fisso di valori |
List<Entity> | Lista di entitΓ | 1:N |
ManyToMany | Molti-a-molti | Richiede tabella di join |
Esempio
Creazione dell'entitΓ Person:
ββββββ¬βββββ¬ββββββββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββ¬βββββββββββββββββ β PK β FK β Property name β Type β Length β Required β Auto-increment β ββββββΌβββββΌββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββΌβββββββββββββββββ€ β x β β Id β int β β x β x β ββββββΌβββββΌββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββΌβββββββββββββββββ€ β β β Name β string β 100 β x β β ββββββ΄βββββ΄ββββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ΄βββββββββββββββββ
Struttura generata da Lino:
<ProjectName>/
βββ src/
βββ Services/
βββ <ServiceName>/
βββ Domain/
βββ <ProjectName>.<ServiceName>.Domain.csproj
βββ Aggregates/
βββ People/
βββ Person.cs
βββ Errors/
β βββ PersonErrors.cs
βββ Repositories/
β βββ IPersonRepository.cs
βββ Resources/
βββ Person/
βββ PersonResources.resx
βββ PersonResources.en.resx
βββ PersonResources.pt-BR.resx
Dopo aver definito le tue entitΓ , usa Lino stesso per gestire le Migrations e mantenere sincronizzato il database. Questo processo sarΓ trattato in dettaglio nella sezione Layer di Persistenza.
Flusso attuale di evoluzione
Oltre al flusso interattivo con lino entity new, la CLI consente anche di rendere esplicita l'intenzione nel comando quando servizio, modulo e nome sono giΓ noti:
lino entity new --name <EntityName> --service <ServiceName> --module <ModuleName> lino entity edit --service <ServiceName> --module <ModuleName> --entity <EntityName> lino entity list --service <ServiceName> --module <ModuleName>
Usare entity list prima di creare nuovi concetti in moduli grandi. Durante le modifiche, verificare identificatore, proprietΓ , obbligatorietΓ , lunghezza, relazioni, indici, ownership, tenant e impatto sulle migrations.
Strongly Typed IDs, ownership e invarianti
Quando gli Strongly Typed IDs sono abilitati, l'identificatore diventa un tipo dedicato, come ProductId, riducendo scambi accidentali tra IDs di entitΓ diverse. Le relazioni devono riflettere il reale ownership del dominio: non ogni riferimento deve diventare una navigazione diretta; spesso un identificatore, una shadow entity, un evento di integrazione o una query esplicita protegge meglio i confini tra moduli.
Collocare le invarianti nel dominio, non solo nella UI, nel database o nei validators. Dopo aver modificato le entitΓ , eseguire una build, rivedere il diff e generare o aggiornare le migrations.
Value Objects
Un Value Object rappresenta un concetto di dominio definito solo dai suoi attributi β non possiede unβidentitΓ propria. Due Value Objects sono considerati uguali se tutti i loro valori sono uguali.
Caratteristiche principali:
- Immutabili dopo la creazione.
- Non possiedono
Id.
Creare un Value Object con Lino
Esegui:
lino value-object new
Il CLI chiederΓ :
- Servizio β Servizio in cui sarΓ creato lβoggetto.
- Modulo β Modulo in cui sarΓ creato lβoggetto (solo nei servizi modulari).
- Posizione β Radice del dominio o specifico aggregato.
- Nome dellβValue Object.
Quindi definisci i campi che compongono lβoggetto.
Tipi di campo disponibili
| Tipo | Descrizione | Note |
|---|---|---|
short | Intero a 16 bit | -32.768 β 32.767 |
int | Intero a 32 bit | -2.147.483.648 β 2.147.483.647 |
long | Intero a 64 bit | -9.223.372.036.854.775.808 β 9.223.372.036.854.775.807 |
string | Testo | Fino a ~2 miliardi di caratteri |
bool | Booleano | true/false |
decimal | Decimale preciso | Valori monetari |
float | Virgola mobile (32 bit) | β 6β9 cifre |
double | Virgola mobile (64 bit) | β 15β17 cifre |
DateTime | Data/ora | Include fuso orario |
DateOnly | Solo data | C# 10+ |
TimeOnly | Solo ora | C# 10+ |
Esempio
Value Object Address:
βββββββββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββ β Property name β Type β Length β Required β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β Street β string β 100 β x β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β Number β string β 10 β x β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β Neighborhood β string β 50 β β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β City β string β 100 β x β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β State β string β 2 β x β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β PostalCode β string β 20 β x β βββββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€ β Country β string β 100 β x β βββββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ
Struttura dei file generata (aggregato Person):
<ProjectName>/
βββ src/
βββ Services/
βββ <ServiceName>/
βββ Domain/
βββ <ProjectName>.<ServiceName>.Domain.csproj
βββ Aggregates/
βββ People/
βββ Person.cs
βββ ValueObjects/
β βββ Address.cs
βββ Errors/
β βββ AddressErrors.cs
β βββ PersonErrors.cs
βββ Repositories/
β βββ IPersonRepository.cs
βββ Resources/
βββ Address/
β βββ AddressResources.resx
β βββ AddressResources.en.resx
β βββ AddressResources.pt-BR.resx
βββ Person/
βββ PersonResources.resx
βββ PersonResources.en.resx
βββ PersonResources.pt-BR.resx
Come per le entitΓ , le Migrations possono essere gestite da Lino per mantenere il modello dati sincronizzato.
Flusso attuale di evoluzione
Oltre al comando interattivo, usare i parametri quando si sa giΓ dove il concetto deve nascere:
lino value-object new --name <ValueObjectName> --service <ServiceName> --module <ModuleName> lino value-object edit --service <ServiceName> --module <ModuleName> --value-object <ValueObjectName> lino value-object list --service <ServiceName> --module <ModuleName>
Quando una proprietΓ di entitΓ Γ¨ dichiarata come ValueObject, Lino puΓ² riutilizzare un Value Object esistente o crearne uno nuovo durante la modellazione dell'entitΓ . Preferire questa opzione quando l'insieme di campi rappresenta un unico concetto, come denaro, indirizzo, dimensioni, periodo o documento.
Persistenza, UI e localizzazione
Lino mappa le proprietΓ dei Value Objects tramite il layer di persistenza del modulo e porta i metadati di visualizzazione nei resources. Questo abilita labels e messaggi localizzati per valori annidati. Il Value Object appartiene al modulo in cui Γ¨ stato generato; non diventa un contratto condiviso tra moduli senza un'integrazione esplicita.
Proteggere le invarianti nel tipo stesso: valori negativi, valuta non valida, periodi invertiti o documenti malformati non devono nascere validi.
Enumerazioni
Le enumerazioni nel DDD possono andare oltre i tradizionali enum di C#. Possono essere oggetti ricchi
che rappresentano stati fissi, contenenti validazioni, metodi ausiliari e persino comportamenti.
Motivazione:
- I
enumdi C# sono limitati a un valore intero o stringa. - Modellare un'Enumeration come classe offre maggiore flessibilitΓ ed espressivitΓ .
Caratteristiche principali:
- Sono classi che ereditano da una base comune e incapsulano
IdeNome. - Permettono di aggiungere validazioni, metodi ausiliari e comportamenti.
Creare un'enumerazione con Lino
Esegui:
lino enumeration new
L'assistente richiederΓ :
- Servizio.
- Modulo (se applicabile).
- Posizione β radice del dominio o aggregato.
- Nome dell'enumerazione.
- Tipo β
enumtradizionale o Smart Enum (class). - Archiviazione β
intostringnel database.
Esempio
Enumerazione PersonStatus:
βββββββββ¬ββββββββββββ¬βββββββββββββββ β Value β Name β Display Name β βββββββββΌββββββββββββΌβββββββββββββββ€ β 1 β Active β Active β βββββββββΌββββββββββββΌβββββββββββββββ€ β 2 β Inactive β Inactive β βββββββββΌββββββββββββΌβββββββββββββββ€ β 3 β Suspended β Suspended β βββββββββΌββββββββββββΌβββββββββββββββ€ β 4 β Deleted β Deleted β βββββββββ΄ββββββββββββ΄βββββββββββββββ
Struttura generata:
<ProjectName>/
βββ src/
βββ Services/
βββ <ServiceName>/
βββ Domain/
βββ <ProjectName>.<ServiceName>.Domain.csproj
βββ Aggregates/
βββ People/
βββ Person.cs
βββ Enums/
β βββ PersonStatus.cs
βββ ValueObjects/
β βββ Address.cs
βββ Errors/
β βββ AddressErrors.cs
β βββ PersonErrors.cs
βββ Repositories/
β βββ IPersonRepository.cs
βββ Resources/
βββ Address/
β βββ AddressResources.resx
β βββ AddressResources.en.resx
β βββ AddressResources.pt-BR.resx
βββ Person/
β βββ PersonResources.resx
β βββ PersonResources.en.resx
β βββ PersonResources.pt-BR.resx
βββ PersonStatus/
βββ PersonStatusResources.resx
βββ PersonStatusResources.en.resx
βββ PersonStatusResources.pt-BR.resx
Archiviare il valore di un'enumerazione come string Γ¨ valido e puΓ² migliorare la leggibilitΓ , ma tende ad essere meno efficiente in termini di prestazioni e spazio di archiviazione.
Per questo, consigliamo di archiviare il valore come int e, per mantenere l'integritΓ referenziale e facilitare la manutenzione, creare un'entitΓ (tabella) ausiliaria dove la chiave primaria corrisponda al valore dell'enumerazione.
Dopo aver definito le enumerazioni, usa Lino per generare e applicare le Migrations, garantendo che il database rifletta il modello di dominio. Vedi i dettagli nella sezione Layer di Persistenza.
Flusso attuale di evoluzione
Oltre al comando interattivo, usare i parametri quando posizione e nome sono giΓ definiti:
lino enumeration new --name <EnumerationName> --service <ServiceName> --module <ModuleName> lino enumeration edit --service <ServiceName> --module <ModuleName> --enumeration <EnumerationName> lino enumeration list --service <ServiceName> --module <ModuleName>
Enumeration, entitΓ o configurazione
| Usare enumeration | Usare entitΓ o configurazione |
|---|---|
| Stato dell'ordine, stato di pubblicazione, tipo tecnico di integrazione | Categoria gestibile dall'utente, motivo configurabile per tenant, fornitore gestito nel backoffice |
| Valori versionati insieme al codice | Valori modificati a runtime o controllati da permessi |
| Regola semplice o piccolo comportamento per valore | Ciclo di vita, audit, traduzione dinamica o relazione propria |
Se la enumeration espone un nome visualizzato, Lino puΓ² generare resources per labels localizzate. Se viene persistita o usata come seed data, rivedere le migrations dopo aver aggiunto, rimosso o rinominato valori. La coerenza tra codice, database e UI fa parte della modifica di dominio.
