2016-06-08 7 views
0

Ich versuche mein Bestes, um eine Web-App zu erstellen, um mich mit DDD und ValueObjects vertraut zu machen. Bis jetzt läuft alles gut, ich frage mich nur, ob ich meine Wertobjekte korrekt implementiere. Hier ist mein aktueller Zustand (Code gestrippt von nicht wesentlichen Teilen):ddd - Ist meine ValueObject-Implementierung korrekt?

class User { 

    private $id; 

    /** 
    * @var Credentials 
    */ 
    public $credentials; 

    public function getId() { return $this->id; } 

} 

class Credentials { 

    private $username; 
    private $password; 

    public function __construct($username, $password) { // no need to detail } 

    public function changePassword($newPassword) { 
     return new self($this->username, $newPassword); 
    } 
} 

Also, wenn ich möchte, dass meine Benutzer-Kennwort aktualisieren, ich habe $user->credentials = $user->credentials->changePassword("newPassword");

zu tun Ist das richtig? Soll ich einen Getter und einen Setter für die Eigenschaft $credentials meiner Benutzerklasse erstellen? Sollte ich die changePassword() Methode in der Benutzerklasse setzen?

Jede Hilfe wird sehr geschätzt!

Antwort

3
$user->credentials = $user->credentials->changePassword("newPassword"); 

Das ist die richtige Idee.

In dieser Implementierung sind $ username und $ password Teil des Status von Credentials. Du willst, dass dieser Zustand unveränderlich ist; Es sollte keinen Code geben, der diesen Zustand ändert, nachdem der Konstruktor beendet wurde.

Sie müssen diesen Zustand jedoch irgendwie freilegen; Unveränderliche Nur-Schreib-Objekte bieten keinen sehr hohen Geschäftswert. Das Implementieren von Werttypen als eine Sammlung unveränderlicher öffentlicher Eigenschaften ist ein gängiges Idiom. Here's eine alte Diskussion darüber, wie man das in PHP macht. Alternativ können Sie Aufrufe implementieren, die es dem Credentials-Objekt ermöglichen, Kopien dieses Status an andere Objekte zu übergeben.

Sollte ich einen Getter und einen Setter für die Eigenschaft $ Credentials meiner Benutzerklasse erstellen?

Nicht normalerweise - Ihre Implementierung von Benutzer hat eine Id-Eigenschaft, die es wie eine Entität aussieht. Entitäten erlauben nur sehr selten ihren inneren Zustand zu entkommen. Vor allem Setter sind vielen Neunen nahe, nie eine gute Idee - der Punkt von Entitäten ist, dass sie ihre eigenen Datenbeschränkungen erzwingen können; Sie sind dafür verantwortlich, dass alle Änderungen ihres Status die Geschäftsinvariante erfüllen.

Ein Getter, der Zugriff auf unveränderlichen Status bietet, wird wahrscheinlich keine Probleme verursachen; Ein Getter, der es dem Anrufer ermöglicht, zu einem veränderlichen Zustand zu navigieren, ist besorgniserregend.

ABER: Getter sind nicht besonders ausdrucksstark - eine Abfrage, die möglicherweise von einem Domain-Service unterstützt wird, ist oft eine flexiblere Wahl

vergleichen:

password = user.password 
strength = calulatatePasswordStrength(password.toString) 

zu

password = user.password 
strength = password.strength(calculator) 

bis

Sollte ich die changePassword() -Methode in der Benutzerklasse setzen?

Angenommen, Sie unterstützen diesen Anwendungsfall, ja. Der Credentials-Wert weiß, wie ein neuer Status von Anmeldeinformationen aus einem alten Status berechnet wird. Die Benutzereinheit kann überprüfen, ob der Benutzer in seinem aktuellen Status die Anmeldeinformationen auf diese Weise ändern darf.

(Beispiel: Eine naive Sicherheitsimplementierung kann verfolgen, wann die Anmeldeinformationen zuletzt geändert wurden und Richtlinien, die Kennwortänderungen einschränken, wobei die richtige Richtlinie von anderen Eigenschaften des Benutzers abhängt. Das ist viel zu viel Arbeit für die Anmeldeinformationen Objekt selbst zu tun.)

Insbesondere die Tatsache, dass das Kennwort des Benutzers tatsächlich eine Reihe von Status in einem Credentials-Wertobjekt ist, ist ein Implementierungsdetail, das nicht dem Code ausgesetzt werden soll, der Benutzer aufruft . Mit anderen Worten, wenn Benutzer die Schnittstelle IUser implementieren würde (Isolierung der Implementierungsdetails), würde ich erwarten, dass IUser verspricht, dass eine Methode changePassword ($ password) verfügbar sein wird.

+0

Okay, scheint mir jetzt klarer. Ich werde eine changePassword() -Methode in meiner Benutzerklasse erstellen und die Eigenschaft dann privat machen, das macht den Code ein bisschen schöner und leichter zu lesen. Danke vielmals ! – Lucio

0

Fast würde ich sagen. Der Schlüssel zu einem Wertobjekt ist ein Wert. I.e. Sie können es mit einem anderen Objekt des gleichen Typs vergleichen und bestimmen, ob es sich durch seinen Wert unterscheidet. In diesem Fall würde ich erwarten, in der Lage zu sein, eine Credential zu sehen, wo username = x und password = x einer anderen Berechtigung mit denselben Werten "gleich" wären.

Das bedeutet nicht unbedingt, dass Sie Getter auf den Feldern brauchen.

Ich habe, was ich denke, ist ein Spaß Code Kata um dieses Thema auf meinem Blog. Sie können es hier finden Why Private C# Variables are Not as Private as you Thought

Das Wertobjekt kata ist in der Nähe der Unterseite. Ich hoffe, das hilft.

+0

Ich habe mir deine Kata angeschaut, ohne Probleme. Fakt ist, wenn ein ValueObject innerhalb einer Entity ist und ich darauf zugreifen muss. Ich habe die Angewohnheit, Getter/Setter für alles zu schaffen, und ich versuche, es zu verlieren. Es scheint jedoch so einfach zu sein, '$ user-> changePassword ($ password)' als '$ user-> credentials = $ user-> credentials-> changePassword ($ password)' ... – Lucio

+0

Benötigen Sie das Credentials-Objekt unter Alles in diesem Fall? Wird es mehr Funktionalität haben? – Codescribler

+0

Eigentlich denke ich nicht, momentan nicht. Ich dachte, ich würde eine '$ E-Mail'-Eigenschaft setzen, aber es passt nicht gut darin, da es mehr eine" Profil "-Eigenschaft in meinem Projekt ist – Lucio