2014-06-26 38 views
15

Ich suche Ratschläge, wo Validierungsregeln für Domain-Entitäten und Best Practices für die Implementierung hinzugefügt werden. Ich habe gesucht und nicht gefunden, wonach ich gesucht habe, oder ich habe es verpasst.DDD-Invarianten Geschäftsregeln und Validierung

Ich würde gerne wissen, was der empfohlene Weg für die Validierung ist, dass Eigenschaften nicht null sind, in einem bestimmten Bereich, oder Länge, etc ... Ich habe mehrere Möglichkeiten gesehen mit einer IsValid() und andere Diskussionen über Durchsetzung in Der Konstruktor, so dass die Entity niemals in einem ungültigen Zustand ist oder Preprocessing und Postprocessing verwendet, und andere, die FluentValidation api verwenden, wie sich Invarianten auf DRY und SRP auswirken.

Kann mir jemand ein gutes Beispiel dafür geben, wo diese Art von Prüfungen zu platzieren sind, wenn ein App-Service, beschränkter Kontext, Domänen-Service, Aggregatwurzel, Entitätsüberlagerung verwendet wird. Wohin geht das und was ist der beste Ansatz?

Danke.

+0

Ein Beispiel: https://github.com/szjani/predaddy-issuetracker-sample/blob/master/src/hu/szjani/domain/issue/Issue.php – inf3rno

+0

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

Antwort

32

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.

+3

+1 Dies ist möglicherweise das beste Beispiel dafür, wie die meisten meiner Validierungsanforderungen eingekapselt werden, die ich je gelesen habe. –

+3

+1 es ist schön, eine Antwort von jemandem zu sehen, der DDD wirklich versteht, dies ist eine Seltenheit auf StackOverflow !! – MattDavey

+0

@AdrianThompsonPhillips wenn Sie diese Ideen mögen, schauen Sie auch [dieses Video von NDC] (http://vimeo.com/97507575) – MattDavey