Domänenmodellierung

Im Herzen jeder domΓ€nenorientierten Anwendung steht das Modell, das das zentrale Wissen und die GeschΓ€ftsregeln des Systems reprΓ€sentiert. Das DomΓ€nenmodell gut zu gestalten bedeutet, Konzepte der realen Welt in ausdrucksstarke, zusammenhΓ€ngende und konsistente Softwarestrukturen zu ΓΌbersetzen.

EntitΓ€ten

Eine EntitΓ€t ist ein Objekt, das hauptsΓ€chlich durch seine IdentitΓ€t definiert ist und nicht nur durch seine Attribute. Auch wenn sich die Attribute im Laufe der Zeit Γ€ndern, bleibt die IdentitΓ€t einer EntitΓ€t gleich.

Hauptmerkmale:

  • Hat eine eindeutige IdentitΓ€t (normalerweise ein Id).
  • Wichtig ist, wer die EntitΓ€t ist, nicht nur was sie enthΓ€lt.
  • Die Attribute kΓΆnnen sich im Laufe der Zeit Γ€ndern.

Erstellen einer EntitΓ€t mit Lino

Um eine neue EntitΓ€t mit Lino zu erstellen, fΓΌhren Sie aus:

lino entity new

Der CLI-Assistent fragt nach:

  • Dienst – Dienst, in dem die EntitΓ€t erstellt wird.
  • Modul – Modul, in dem die EntitΓ€t erstellt wird (nur bei modularen Diensten).
  • EntitΓ€tsname – Name, der in der DomΓ€ne und der Datenbanktabelle verwendet wird.

Dann definieren Sie die Felder, aus denen die EntitΓ€t besteht, und konfigurieren jedes einzelne.

VerfΓΌgbare Feldtypen

Typ Beschreibung Bereich / Anmerkungen
short16-Bit Ganzzahl-32β€―768 β†’ 32β€―767
int32-Bit Ganzzahl-2β€―147β€―483β€―648 β†’ 2β€―147β€―483β€―647
long64-Bit Ganzzahl-9β€―223β€―372β€―036β€―854β€―775β€―808 β†’ 9β€―223β€―372β€―036β€―854β€―775β€―807
stringTextBis zu ~2 Milliarden Zeichen
boolBoolescher Werttrue oder false
GuidEindeutiger globaler BezeichnerVerteilte Eindeutigkeit
decimalDezimalzahl mit hoher GenauigkeitIdeal fΓΌr monetΓ€re Werte
floatGleitkommazahl (32 Bit)β‰ˆ 6–9 Stellen Genauigkeit
doubleGleitkommazahl (64 Bit)β‰ˆ 15–17 Stellen Genauigkeit
DateTimeDatum und UhrzeitBeinhaltet Zeitzone
DateOnlyNur Datum (C# 10+)–
TimeOnlyNur Uhrzeit (C# 10+)–
EntityVerweis auf eine andere EntitΓ€t1:1 oder 1:N
Value ObjectUnverΓ€nderliches Wertobjektz.B. Adresse, CPF
EnumAufzΓ€hlungFeste Menge von Werten
List<Entity>Liste von EntitΓ€ten1:N
ManyToManyViele-zu-vieleErfordert VerknΓΌpfungstabelle

Beispiel

Erstellen der EntitΓ€t Person:

β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PK β”‚ FK β”‚ Property name β”‚ Type   β”‚ Length β”‚ Required  β”‚ Auto-increment β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ x  β”‚    β”‚ Id            β”‚ int    β”‚        β”‚     x     β”‚       x        β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    β”‚    β”‚ Name          β”‚ string β”‚  100   β”‚     x     β”‚                β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Vom Lino generierte Struktur:

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

Nachdem Sie Ihre EntitΓ€ten definiert haben, verwenden Sie Lino, um die Migrations zu verwalten und die Datenbank synchron zu halten. Dieser Prozess wird im Abschnitt Persistenzschicht ausfΓΌhrlich behandelt.

Wertobjekte

Ein Wertobjekt reprΓ€sentiert ein DomΓ€nenkonzept, das ausschließlich durch seine Attribute definiert wird – es besitzt keine eigene IdentitΓ€t. Zwei Wertobjekte gelten als gleich, wenn alle ihre Werte ΓΌbereinstimmen.

Hauptmerkmale:

  • UnverΓ€nderlich nach der Erstellung.
  • Besitzen keine Id.

Erstellen eines Wertobjekts mit Lino

FΓΌhren Sie aus:

lino value-object new

Das CLI wird fragen nach:

  • Service – Service, in dem das Objekt erstellt wird.
  • Modul – Modul, in dem das Objekt erstellt wird (nur in modularen Services).
  • Ort – DomΓ€nenwurzel oder spezifisches Aggregat.
  • Name des Wertobjekts.

Definieren Sie anschließend die Felder, die das Objekt ausmachen.

VerfΓΌgbare Feldtypen

TypBeschreibungAnmerkungen
short16-Bit-Ganzzahl-32.768 β†’ 32.767
int32-Bit-Ganzzahl-2.147.483.648 β†’ 2.147.483.647
long64-Bit-Ganzzahl-9.223.372.036.854.775.808 β†’ 9.223.372.036.854.775.807
stringTextBis zu ~2 Milliarden Zeichen
boolBooleantrue/false
decimalPrΓ€zise DezimalzahlMonetΓ€re Werte
floatGleitkommazahl (32 Bit)β‰ˆ 6–9 Stellen
doubleGleitkommazahl (64 Bit)β‰ˆ 15–17 Stellen
DateTimeDatum/UhrzeitEnthΓ€lt Zeitzone
DateOnlyNur DatumC# 10+
TimeOnlyNur UhrzeitC# 10+

Beispiel

Wertobjekt 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     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Generierte Dateistruktur (Aggregat 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

Wie bei EntitΓ€ten kΓΆnnen Migrations von Lino verwaltet werden, um das Datenmodell synchron zu halten.

Enumeration

Enumeration in DDD kΓΆnnen ΓΌber die traditionellen enum-Typen von C# hinausgehen. Sie kΓΆnnen reichhaltige Objekte sein, die feste ZustΓ€nde reprΓ€sentieren, Validierungen, Hilfsmethoden und sogar Verhalten enthalten.

Motivation:

  • Die C#-enum-Typen sind auf einen ganzzahligen oder string-Wert beschrΓ€nkt.
  • Das Modellieren einer Enumeration als Klasse bietet mehr FlexibilitΓ€t und Ausdruckskraft.

Hauptmerkmale:

  • Es sind Klassen, die von einer gemeinsamen Basis erben und Id und Name kapseln.
  • Sie erlauben das HinzufΓΌgen von Validierungen, Hilfsmethoden und Verhalten.

Erstellen einer Enumeration mit Lino

FΓΌhren Sie aus:

lino enum new

Der Assistent wird nach Folgendem fragen:

  • Service.
  • Modul (falls zutreffend).
  • Ort – Root des DomΓ€nens oder Aggregats.
  • Name der Enumeration.
  • Typ – traditionelles enum oder Smart Enum (class).
  • Speicherung – int oder string in der Datenbank.

Beispiel

Enumeration PersonStatus:

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

Generierte Struktur:

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

Das Speichern des Werts einer Enumeration als string ist gΓΌltig und kann die Lesbarkeit verbessern, ist jedoch in Bezug auf Leistung und Speicher ineffizienter. Daher empfehlen wir, den Wert als int zu speichern und zur Wahrung der referenziellen IntegritΓ€t und zur Vereinfachung der Wartung eine HilfsentitΓ€t (Tabelle) zu erstellen, bei der der PrimΓ€rschlΓΌssel dem Wert der Enumeration entspricht.

Nachdem Sie die Enumerationen definiert haben, verwenden Sie Lino, um die Migrations zu generieren und anzuwenden, damit die Datenbank das DomΓ€nenmodell widerspiegelt. Details finden Sie im Abschnitt Persistenzschicht.

Ein unbehandelter Fehler ist aufgetreten. Aktualisieren πŸ—™