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
shortIntero a 16 bit-32.768 β†’ 32.767
intIntero a 32 bit-2.147.483.648 β†’ 2.147.483.647
longIntero a 64 bit-9.223.372.036.854.775.808 β†’ 9.223.372.036.854.775.807
stringTestoFino a circa 2 miliardi di caratteri
boolValore booleanotrue o false
GuidIdentificatore globale univocoUnicitΓ  distribuita
decimalNumero decimale ad alta precisioneIdeale per valori monetari
floatPunto flottante (32 bit)β‰ˆ 6–9 cifre di precisione
doublePunto flottante (64 bit)β‰ˆ 15–17 cifre di precisione
DateTimeData e oraInclude il fuso orario
DateOnlySolo data (C# 10+)–
TimeOnlySolo ora (C# 10+)–
EntityRiferimento a un'altra entitΓ 1:1 o 1:N
Value ObjectOggetto di valore immutabileEs.: Indirizzo, CPF
EnumEnumerazioneInsieme fisso di valori
List<Entity>Lista di entitΓ 1:N
ManyToManyMolti-a-moltiRichiede 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:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Domain/
                β”œβ”€β”€ MyApp.MyService.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.

Oggetti di Valore

Un oggetto di valore rappresenta un concetto di dominio definito solo dai suoi attributi – non possiede un’identitΓ  propria. Due oggetti di valore sono considerati uguali se tutti i loro valori sono uguali.

Caratteristiche principali:

  • Immutabili dopo la creazione.
  • Non possiedono Id.

Creare un oggetto di valore 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’oggetto di valore.

Quindi definisci i campi che compongono l’oggetto.

Tipi di campo disponibili

TipoDescrizioneNote
shortIntero a 16 bit-32.768 β†’ 32.767
intIntero a 32 bit-2.147.483.648 β†’ 2.147.483.647
longIntero a 64 bit-9.223.372.036.854.775.808 β†’ 9.223.372.036.854.775.807
stringTestoFino a ~2 miliardi di caratteri
boolBooleanotrue/false
decimalDecimale precisoValori monetari
floatVirgola mobile (32 bit)β‰ˆ 6–9 cifre
doubleVirgola mobile (64 bit)β‰ˆ 15–17 cifre
DateTimeData/oraInclude fuso orario
DateOnlySolo dataC# 10+
TimeOnlySolo oraC# 10+

Esempio

Oggetto di valore 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):

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Domain/
                β”œβ”€β”€ MyApp.MyService.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.

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 enum di 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 Id e Nome.
  • Permettono di aggiungere validazioni, metodi ausiliari e comportamenti.

Creare un'enumerazione con Lino

Esegui:

lino enum new

L'assistente richiederΓ :

  • Servizio.
  • Modulo (se applicabile).
  • Posizione – radice del dominio o aggregato.
  • Nome dell'enumerazione.
  • Tipo – enum tradizionale o Smart Enum (class).
  • Archiviazione – int o string nel database.

Esempio

Enumerazione PersonStatus:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Value β”‚ Name      β”‚ Display Name β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1     β”‚ Active    β”‚ Active       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 2     β”‚ Inactive  β”‚ Inactive     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 3     β”‚ Suspended β”‚ Suspended    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 4     β”‚ Deleted   β”‚ Deleted      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Struttura generata:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Domain/
                β”œβ”€β”€ MyApp.MyService.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.

Si è verificato un errore non gestito. Ricarica πŸ—™