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 |
|---|---|---|
short | 16-Bit Ganzzahl | -32β―768 β 32β―767 |
int | 32-Bit Ganzzahl | -2β―147β―483β―648 β 2β―147β―483β―647 |
long | 64-Bit Ganzzahl | -9β―223β―372β―036β―854β―775β―808 β 9β―223β―372β―036β―854β―775β―807 |
string | Text | Bis zu ~2 Milliarden Zeichen |
bool | Boolescher Wert | true oder false |
Guid | Eindeutiger globaler Bezeichner | Verteilte Eindeutigkeit |
decimal | Dezimalzahl mit hoher Genauigkeit | Ideal fΓΌr monetΓ€re Werte |
float | Gleitkommazahl (32 Bit) | β 6β9 Stellen Genauigkeit |
double | Gleitkommazahl (64 Bit) | β 15β17 Stellen Genauigkeit |
DateTime | Datum und Uhrzeit | Beinhaltet Zeitzone |
DateOnly | Nur Datum (C# 10+) | β |
TimeOnly | Nur Uhrzeit (C# 10+) | β |
Entity | Verweis auf eine andere EntitΓ€t | 1:1 oder 1:N |
Value Object | UnverΓ€nderliches Value Object | z.B. Adresse, CPF |
Enum | AufzΓ€hlung | Feste Menge von Werten |
List<Entity> | Liste von EntitΓ€ten | 1:N |
ManyToMany | Viele-zu-viele | Erfordert 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:
<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
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.
Aktueller Evolutionsablauf
Neben dem interaktiven Ablauf mit lino entity new kann die CLI die Absicht auch direkt im Befehl ausdrΓΌcken, wenn Service, Modul und Name bereits bekannt sind:
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>
Verwenden Sie entity list, bevor Sie in groΓen Modulen neue Konzepte erstellen. PrΓΌfen Sie bei Γnderungen Identifier, Properties, Pflichtangaben, LΓ€nge, Beziehungen, Indizes, Ownership, Tenant und Auswirkungen auf Migrations.
Strongly Typed IDs, Ownership und Invarianten
Wenn Strongly Typed IDs aktiviert sind, wird der Identifier zu einem dedizierten Typ, etwa ProductId, wodurch versehentliche Vertauschungen von IDs verschiedener EntitΓ€ten reduziert werden. Beziehungen sollten das tatsΓ€chliche Ownership im DomΓ€nenmodell widerspiegeln: Nicht jede Referenz muss zu einer direkten Navigation werden; oft schΓΌtzen ein Identifier, eine Shadow Entity, ein Integration Event oder eine explizite Abfrage Modulgrenzen besser.
Legen Sie Invarianten in die DomΓ€ne, nicht nur in die UI, Datenbank oder Validators. FΓΌhren Sie nach Γnderungen an EntitΓ€ten einen Build aus, prΓΌfen Sie den Diff und generieren oder aktualisieren Sie Migrations.
Value Objects
Ein Value Object reprΓ€sentiert ein DomΓ€nenkonzept, das ausschlieΓlich durch seine Attribute definiert wird β es besitzt keine eigene IdentitΓ€t. Zwei Value Objects gelten als gleich, wenn alle ihre Werte ΓΌbereinstimmen.
Hauptmerkmale:
- UnverΓ€nderlich nach der Erstellung.
- Besitzen keine
Id.
Erstellen eines Value Object 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 Value Object.
Definieren Sie anschlieΓend die Felder, die das Objekt ausmachen.
VerfΓΌgbare Feldtypen
| Typ | Beschreibung | Anmerkungen |
|---|---|---|
short | 16-Bit-Ganzzahl | -32.768 β 32.767 |
int | 32-Bit-Ganzzahl | -2.147.483.648 β 2.147.483.647 |
long | 64-Bit-Ganzzahl | -9.223.372.036.854.775.808 β 9.223.372.036.854.775.807 |
string | Text | Bis zu ~2 Milliarden Zeichen |
bool | Boolean | true/false |
decimal | PrΓ€zise Dezimalzahl | MonetΓ€re Werte |
float | Gleitkommazahl (32 Bit) | β 6β9 Stellen |
double | Gleitkommazahl (64 Bit) | β 15β17 Stellen |
DateTime | Datum/Uhrzeit | EnthΓ€lt Zeitzone |
DateOnly | Nur Datum | C# 10+ |
TimeOnly | Nur Uhrzeit | C# 10+ |
Beispiel
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 β βββββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ
Generierte Dateistruktur (Aggregat 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
Wie bei EntitΓ€ten kΓΆnnen Migrations von Lino verwaltet werden, um das Datenmodell synchron zu halten.
Aktueller Evolutionsablauf
Neben dem interaktiven Befehl kΓΆnnen Sie Parameter verwenden, wenn bereits klar ist, wo das Konzept entstehen soll:
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>
Wenn eine EntitΓ€tseigenschaft als ValueObject deklariert wird, kann Lino ein vorhandenes Value Object wiederverwenden oder wΓ€hrend der EntitΓ€tsmodellierung ein neues erstellen. Bevorzugen Sie diese Option, wenn die Felder ein gemeinsames Konzept darstellen, etwa Geld, Adresse, Abmessungen, Zeitraum oder Dokument.
Persistenz, UI und Lokalisierung
Lino mappt Eigenschaften von Value Objects ΓΌber die Persistenzschicht des Moduls und ΓΌbertrΓ€gt Anzeigemetadaten in Resources. Dadurch werden lokalisierte Labels und Meldungen fΓΌr verschachtelte Werte mΓΆglich. Das Value Object gehΓΆrt zu dem Modul, in dem es generiert wurde; ohne explizite Integration wird es nicht zu einem gemeinsam genutzten Vertrag zwischen Modulen.
SchΓΌtzen Sie Invarianten im Typ selbst: Negative Werte, ungΓΌltige WΓ€hrungen, invertierte ZeitrΓ€ume oder fehlerhafte Dokumente sollten nicht gΓΌltig erzeugt werden kΓΆnnen.
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
IdundNamekapseln. - Sie erlauben das HinzufΓΌgen von Validierungen, Hilfsmethoden und Verhalten.
Erstellen einer Enumeration mit Lino
FΓΌhren Sie aus:
lino enumeration new
Der Assistent wird nach Folgendem fragen:
- Service.
- Modul (falls zutreffend).
- Ort β Root des DomΓ€nens oder Aggregats.
- Name der Enumeration.
- Typ β traditionelles
enumoder Smart Enum (class). - Speicherung β
intoderstringin der Datenbank.
Beispiel
Enumeration PersonStatus:
βββββββββ¬ββββββββββββ¬βββββββββββββββ β Value β Name β Display Name β βββββββββΌββββββββββββΌβββββββββββββββ€ β 1 β Active β Active β βββββββββΌββββββββββββΌβββββββββββββββ€ β 2 β Inactive β Inactive β βββββββββΌββββββββββββΌβββββββββββββββ€ β 3 β Suspended β Suspended β βββββββββΌββββββββββββΌβββββββββββββββ€ β 4 β Deleted β Deleted β βββββββββ΄ββββββββββββ΄βββββββββββββββ
Generierte Struktur:
<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
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.
Aktueller Evolutionsablauf
Neben dem interaktiven Befehl kΓΆnnen Sie Parameter verwenden, wenn Speicherort und Name bereits feststehen:
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Γ€t oder Konfiguration
| Enumeration verwenden | EntitΓ€t oder Konfiguration verwenden |
|---|---|
| Bestellstatus, VerΓΆffentlichungsstatus, technischer Integrationstyp | Vom Benutzer verwaltete Kategorie, pro Tenant konfigurierbarer Grund, im Backoffice verwalteter Lieferant |
| Mit dem Code versionierte Werte | Zur Laufzeit geΓ€nderte oder durch Berechtigungen gesteuerte Werte |
| Einfache Regel oder kleines Verhalten pro Wert | Lebenszyklus, Audit, dynamische Γbersetzung oder eigene Beziehung |
Wenn die Enumeration einen Anzeigenamen bereitstellt, kann Lino Resources fΓΌr lokalisierte Labels generieren. Wenn sie persistiert oder als Seed Data verwendet wird, prΓΌfen Sie Migrations nach dem HinzufΓΌgen, Entfernen oder Umbenennen von Werten. Konsistenz zwischen Code, Datenbank und UI ist Teil der DomΓ€nenΓ€nderung.
