Bei der Modellierung Ihrer Domänenentität sollten Sie die Implikationen der realen Welt berücksichtigen. Nehmen wir an, Sie haben es mit einer Employee
Entität zu tun.
Mitarbeiter müssen einen Namen
Wir wissen, dass in der realen Welt Mitarbeiter müssen immer einen Namen haben. Es ist unmöglich, dass ein Mitarbeiter keinen Namen hat. Mit anderen Worten, man kann einen Mitarbeiter nicht konstruieren, ohne seinen Namen anzugeben. Verwenden Sie also parametrisierte Konstruktoren! Wir wissen auch, dass sich der Name eines Mitarbeiters nicht ändern kann. Daher verhindern wir, dass dies geschieht, indem ein privater Setter erstellt wird. Die Verwendung des .NET-Typsystems zur Überprüfung Ihres Mitarbeiters ist eine sehr starke Form der Validierung.
public string Name { get; private set; }
public Employee(string name)
{
Name = name;
}
Gültige Namen haben einige Regeln
Jetzt beginnt es interessant. Ein Name hat bestimmte Regeln. Nehmen wir den einfachen Weg und nehmen an, dass ein gültiger Name ein Name ist, der nicht null oder leer ist. Im obigen Codebeispiel wird die folgende Geschäftsregel nicht überprüft. An dieser Stelle können wir derzeit noch ungültige Mitarbeiter anlegen! Lassen Sie uns dies verhindern, dass jemals durch eine Änderung unserer Setter vorkommen:
public string Name
{
get
{
return name;
}
private set
{
if (String.IsNullOrWhiteSpace(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
persönlich, als ich es vorziehen, diese Logik im privaten Setter haben im Konstruktor. Der Setter ist nicht vollständig unsichtbar. Die Entität selbst kann sie noch ändern, und wir müssen die Gültigkeit sicherstellen. Werfen Sie auch immer Ausnahmen auf!
Was ist mit dem Aussetzen irgendeiner Form von IsValid()
Methode?
Nehmen Sie die oben genannte Einheit Employee
. Wo und wie würde eine IsValid()
Methode funktionieren?
Würden Sie zulassen, dass ein ungültiger Mitarbeiter erstellt wird, und dann erwarten, dass der Entwickler seine Gültigkeit mit einer IsValid()
Prüfung überprüft? Dies ist ein schwaches Design - bevor Sie es wissen, werden namenlose Mitarbeiter um Ihr System herumfahren und Chaos verursachen.
Aber vielleicht möchten Sie die Namensvalidierungslogik verfügbar machen?
Wir wollen keine Ausnahmen für den Kontrollfluss abfangen. Ausnahmen sind für einen katastrophalen Systemausfall.Wir möchten diese Validierungsregeln auch nicht in unserer Codebasis kopieren. Es ist also keine so schlechte Idee, diese Validierungslogik aufzudecken (aber immer noch nicht die beste!).
Was Sie tun können, ist eine statische IsValidName(string)
Methode bieten:
public static bool IsValidName(string name)
{
return (String.IsNullOrWhiteSpace(value))
}
Unser Eigentum würde jetzt etwas ändern:
public string Name
{
get
{
return name;
}
private set
{
if (!Employee.IsValidName(value))
{
throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
}
name = value;
}
}
Aber es ist etwas faul an diesem Entwurf ...
Wir beginnen nun, Validierungsmethoden für einzelne Eigenschaften unserer Entität zu spawnen. Wenn eine Eigenschaft mit allen möglichen Regeln und Verhaltensweisen verknüpft ist, ist dies möglicherweise ein Zeichen dafür, dass wir ein Wertobjekt dafür erstellen können!
public PersonName : IEquatable<PersonName>
{
public string Name
{
get
{
return name;
}
private set
{
if (!PersonName.IsValid(value))
{
throw new ArgumentOutOfRangeException("value", "Person name cannot be an empty value");
}
name = value;
}
}
private PersonName(string name)
{
Name = name;
}
public static PersonName From(string name)
{
return new PersonName(name);
}
public static bool IsValid(string name)
{
return !String.IsNullOrWhiteSpace(value);
}
// Don't forget to override .Equals
}
Jetzt ist unsere Employee
können Unternehmen vereinfacht werden (I einen Nullreferenzprüfung ausgeschlossen):
public Employee
{
public PersonName Name { get; private set; }
public Employee(PersonName name)
{
Name = name;
}
}
Unser Code-Client kann nun wie folgt aussehen:
if(PersonName.IsValid(name))
{
employee = new Employee(PersonName.From(name));
}
else
{
// Send a validation message to the user or something
}
So Was haben wir hier gemacht?
Wir haben sichergestellt, dass unser Domänenmodell immer konsistent ist. Extrem wichtig. Eine ungültige Entität kann nicht erstellt werden. Darüber hinaus haben wir Wertobjekte verwendet, um weitere "Reichhaltigkeit" bereitzustellen. PersonName
hat dem Client-Code mehr Kontrolle und mehr Power gegeben und hat auch Employee
vereinfacht.
Ein Beispiel: https://github.com/szjani/predaddy-issuetracker-sample/blob/master/src/hu/szjani/domain/issue/Issue.php – inf3rno
Imho sie nicht auswirken sollte SRP. Sie können eine Überprüfung durchführen, bevor Sie eine Domänenobjekteigenschaft festlegen, z. B. in den Befehlen, wenn Sie CQRS verwenden, damit Sie sicher sind, dass sie gültig sind. Sie können Ihre Domänenobjekte mit Anmerkungen versehen und diese Daten zur Überprüfung verwenden. – inf3rno