2012-08-14 3 views
9

Ich arbeite am Umschreiben meiner ASP.NET MVC App mit den Domain-Driven-Design-Prinzipien. Ich versuche meine Benutzereinheit zu validieren. Bis jetzt bin ich in der Lage, Grundregeln zu validieren (wie der Benutzername und das Passwort sind eine Nicht-Null-/Whitespace-Zeichenfolge). Wie auch immer, eine der Regeln muss ich sicherstellen, dass der Benutzername eindeutig ist. Dafür benötige ich jedoch Zugriff auf die Datenbank, was bedeutet, dass ich mein IUserRepository in meine Benutzer-Entity wie folgt einfügen müsste.DDD Domain Model Complex Validierung

Allerdings scheint dies ... gut falsch. Es ist eine schlechte Idee, meine Entität von meinem Repository abhängig zu machen (korrigiere mich, wenn ich falsch liege). Aber der Validierungscode in der Entität ist sinnvoll. Wo ist der beste Ort, um einen komplexen Validierungscode einzugeben?

+2

Siehe auch [diese Antwort] (http://Stackoverflow.com/a/9676307/706456) – oleksii

Antwort

8

Allerdings scheint dies ... gut falsch. Es ist eine schlechte Idee, meine Entität von meinem Repository abhängig zu machen (korrigiere mich, wenn ich falsch liege).

Im Allgemeinen ist die Abhängigkeit vom Repository nicht "falsch", es ist manchmal unvermeidbar. Ich denke jedoch, dass dies eine Ausnahme sein sollte und so weit wie möglich vermieden werden sollte. In Ihrem Szenario können Sie diese Abhängigkeit noch einmal überdenken. Wenn Sie darüber nachdenken, ist "Einzigartigkeit" keine Verantwortung der Entität selbst, da die Entität nichts über andere Entitäten weiß. Warum sollte diese Regel von Entitäten durchgesetzt werden?

Aber der Validierungscode in der Entität ist sinnvoll. Wo ist der beste Ort, um einen komplexen Validierungscode einzugeben?

Ich denke, dass Sie "Validierung" übergeneralisieren können. Ich würde die "Validate" -Methode loswerden und sicherstellen, dass das Objekt überhaupt nicht in den "ungültigen" Zustand gelangt. I answered ähnliche Frage vor ein paar Monaten.

Jetzt zurück zur Eindeutigkeitsregel. Ich denke, dass dies eines der Beispiele ist, bei denen DDD ein wenig "leckt", in einem Sinne, dass die Durchsetzung dieser Geschäftsregel nicht nur im Domänencode ausgedrückt werden kann. Ich würde es so nähern:

// repository 
interface Users { 
    // implementation executes SQL COUNT in case of relation DB 
    bool IsNameUnique(String name); 

    // implementation will call IsNameUnique and throw if it fails 
    void Add(User user); 
} 

Der Client-Code würde wissen, dass vor dem Hinzufügen eines neuen Benutzers, es explizit auf Eindeutigkeit prüfen sollte, sonst wird es zum Absturz bringen. Diese Kombination erzwingt Geschäftsregel im Domänencode, aber es ist normalerweise nicht genug. Als zusätzliche Erzwingungsschicht möchten Sie möglicherweise die UNIQUE-Einschränkung in der Datenbank hinzufügen oder explizit sperren.

4

Doch dies scheint ... auch falsch

Nein, es ist nicht zu bemängeln. Es ist völlig in Ordnung, das Domänenmodell von einem Repository abhängig zu machen. Darüber hinaus haben Sie Ihr Repository hinter einer noch besseren Schnittstelle zusammengefasst.

Oder verwenden Sie nicht Konstruktor Injektion. Übergeben Sie das Repository in der Validate-Methode, wenn es das einzige ist, es braucht:

public class User 
{ 
    public void Validate(IUserRepository repo) 
    { 
     //Basic validation code 
     if (string.IsNullOrEmpty(Username)) 
      throw new ValidationException("Username can not be a null or whitespace characters"); 
     if (string.IsNullOrEmpty(Password)) 
      throw new ValidationException("Password can not be a null or whitespace characters"); 

     //Complex validation code 
     var user = repo.GetUserByUsername(Username); 
     if (user != null && user.id != id) 
      throw new ValidationException("Username must be unique") 
    } 
} 
+0

Vielen Dank für Ihre Antwort. Das Problem ist, dass ich jetzt eine Implementierung von IUserRepository haben muss, um einen Benutzer zu erstellen, was bedeutet, dass ich den IoC verwenden müsste, um die Abhängigkeiten für Entitäten zu injizieren. Dies scheint mehr Code zu sein, als es wert ist, besonders, da ich die Initialisierungsattribute beim Erstellen des Objekts ausnutze. Beispiel var user = new User {Username = "Admin"}; –

+0

Verwenden Sie in diesem Fall keine Konstruktorinjektion. Übergeben Sie das Repository an die Validate-Methode, wenn dies die einzige ist, die es benötigt. –

1

ich mit @oleksii zustimmen, using the specification pattern ist ein besserer Ansatz. Validation hat in verschiedenen Kontexten unterschiedliche Bedeutungen, daher macht es Sinn, dieses Anliegen zu spalten.

8

Ein Muster, das ich in diesen Situationen verwende, ist die Platzierung dieser Art von Validierungslogik im Anwendungsservice.Dies ist zum Teil sinnvoll, weil die Entität User nur für ihre eigene Gültigkeit verantwortlich ist, nicht für die Gültigkeit der Benutzergruppe. Die Application Service-Methode, die den Benutzer erstellt, kann diese Looklike:

public User CreateUser(string userName) 
{ 
    if (this.userRepository.Exists(userName)) 
    throw new Exception(); 
    var user = new User(userName); 
    this.userRepository.Add(user); 
    return user; 
} 

Der Application Service ist eine Abstraktion, die es gibt, unabhängig davon, ob Sie DDD oder nicht beschäftigen und ist daher ein guter Ort, um zu fallen zurück, wenn DDD gibt Reibung.