DDD-Klassenentwurfsdilemma mit Wertobjekten mit DB-ID und -Entitäten

8

Das ist eine lange Frage, also werde ich direkt auf den Punkt kommen. Dies ist Pseudocode zur besseren Veranschaulichung des Problems

DB-Struktur

Benutzer (Benutzer-ID, Name, Nachname)

Adresse (AddressID, UserID, Straße, Stadt, Bundesland, Postleitzahl) = & gt; Viele-zu-eins-Benutzerbeziehung

Telefon (PhoneID, UserID, Number, IsPrimary) = & gt; Viele-zu-eins-Benutzerbeziehung

Domänenklassen

%Vor%

Wir haben also eine sehr einfache Darstellung dieser Domäne und ihrer Modelle.

Meine Frage ist die folgende. Angenommen, ich möchte eine der Adressen aktualisieren oder die Ortskennzahl für eine der Nummern korrigieren, weil sie falsch geschrieben wurde, als sie ursprünglich eingegeben wurde.

Wenn ich Evans Bibel über DDD folge, sollten Wertobjekte unveränderlich sein. Dies bedeutet, dass nach der Erstellung keine Änderungen an den Eigenschaften oder Feldern vorgenommen wurden. Wenn das der Fall ist, dann denke ich, dass keine meiner Klassen ein ValueObject ist, da ich nicht einfach die gesamte ContactInfo-Klasse neu erstellen kann, nur weil ein Teil der Zeichenfolge in der Telefonnummer falsch ist. Also, ich denke, das macht alle meine Klassen Entitäten?

Beachten Sie, dass ich für jede dieser Klassen eine "Persistenz-ID" habe, da sie in einer Datenbank gespeichert sind.

Nehmen wir an, dass ich beschließe, Phone zu einem value-Objekt zu machen, da es einfach ist, es im Konstruktor

neu zu erstellen %Vor%

also, es wäre etwas wie das Hinzufügen einer Methode zu User (agg root) UND Kontaktinfo? (Demeter Gesetz)

wie ...

%Vor%

aber ich muss mich immer noch mit der Persistenz ID beschäftigen ... grrrrr. Was für Kopfschmerzen.

Manchmal fühle ich, wenn ich diese Blogs lese, dass die meisten "ddd-Experten", die Objekte wertschätzen, überstrapaziert werden, oder ich würde sagen, sie würden missbraucht.

Was wäre die beste Lösung für dieses Szenario? Danke

    
Pepito Fernandez 09.12.2012, 01:03
quelle

3 Antworten

3

Eine Entität hat einen einzigartigen und individuellen Lebenszyklus. Es hat Bedeutung, wenn es alleine steht.

Das klassische Beispiel von Order / OrderItem kann dabei helfen. Wenn ein OrderItem eine Entität wird, hätte es einen eigenen Lebenszyklus. Dies macht jedoch keinen allzu großen Sinn, da es ein Teil von Order ist. Dies erscheint immer offensichtlich, wenn man einen Auftrag betrachtet, aber weniger, wenn man sich seine eigenen Klassen ansieht, da es zwischen den Klassen einige Referenzen geben kann. Zum Beispiel repräsentiert OrderItem einige Product , die wir verkaufen. A Product hat einen eigenen Lebenszyklus. Wir können eine unabhängige Liste von Product s haben. Wie wir die Verbindung zwischen einem OrderItem und dem Product modellieren, ist wahrscheinlich eine weitere Diskussion, aber ich würde die benötigten Product -Daten in OrderItem denormalisieren und das ursprüngliche Product.Id ebenfalls speichern.

Also ist die Address -Klasse eine Entity oder ein Value Object? Das ist immer ein interessantes, weil wir diesen Favoriten haben: Es kommt darauf an.

Es wird kontextspezifisch sein. Aber frag dich, ob du eine unabhängige Liste von Address s hast (oder brauchst) und dann nur den Link zu dieser Address in deinem User brauchst. Wenn dies der Fall ist, handelt es sich um eine Entität. Wenn Ihr Address jedoch nur dann sinnvoll ist, wenn es Teil von Ihrem User ist, dann ist es ein Wertobjekt.

Die Tatsache, dass ein Wertobjekt unveränderlich ist, bedeutet nicht, dass Sie mehr als nur das bestimmte Wertobjekt ersetzen müssen. Ich weiß nicht, ob ich eine ContactInfo -Klasse in Ihrem aktuellen Design haben würde, da es nur die zwei Sammlungen umschließt ( Address / PhoneNumber ), aber ich würde es behalten, wenn es mehr gibt (wahrscheinlich ist es). Tauschen Sie einfach das relevante PhoneNumber aus. Wenn Sie etwas wie primär / sekundär haben, dann ist es so einfach wie:

AR.ReplacePrimaryPhoneNumber(new PhoneNumber('...'))

Wenn es sich um eine Liste mit beliebigen Zahlen handelt, wäre ein Remove / Add angemessen.

Jetzt für die Persistenz Id . Du brauchst keinen. Wenn Sie ein primäres / sekundäres Szenario haben, wissen Sie, was Ihr Anwendungsfall ist, und Sie können die relevanten Abfragen in Ihrem DB ausführen (um beispielsweise das primäre PhoneNumber zu aktualisieren). Wenn Sie eine willkürliche Liste haben, können Sie für alle neuen Nummern in meiner Liste hinzufügen und löschen Sie diese Nummern aus der DB, die nicht in meiner Liste enthalten ist ; sonst lösche einfach alle Zahlen und füge alles hinzu, was du hast. Wenn das nach viel Bewegung aussieht, ist es so. Event-Sourcing würde einen Großteil davon in die In-Memory-Verarbeitung verlagern, und das ist etwas, das ich in Zukunft weiter vorantreiben werde.

Ich hoffe, das macht alles Sinn. Sich von der Datenseite der Dinge zu entfernen, ist ziemlich schwierig, aber notwendig. Konzentrieren Sie sich auf die Domäne, als ob Sie keine Datenbank hätten. Wenn Sie Reibung finden, dann tun Sie Ihr Möglichstes, nicht Datenbank-Denken in Ihre Domain zu ziehen, sondern versuchen Sie darüber nachzudenken, wie Sie Ihre Domain sauber halten und trotzdem Ihre Datenbank Ihrer Wahl verwenden können.

    
Eben Roux 09.12.2012, 06:58
quelle
6
  

Wenn ich Evans Bibel über DDD folge, sollten Wertobjekte unveränderlich sein.   Dies bedeutet, dass nach der Erstellung keine Änderungen an den Eigenschaften oder Feldern vorgenommen wurden.   Wenn das der Fall ist, dann denke ich, keiner meiner Klassen ist ein   ValueObject, da ich nicht nur die gesamte ContactInfo-Klasse neu erstellen kann   nur weil ein Teil der Zeichenfolge in der Telefonnummer falsch ist.   Also, ich denke, das macht alle meine Klassen Entitäten?

Während das VO selbst unveränderlich sein kann, existiert ein VO nicht alleine - es ist immer Teil eines Aggregats. Daher kann ein VO unveränderlich sein, aber das Objekt, das auf dieses VO verweist, muss nicht sein. Was mir geholfen hat, VOs zu verstehen, ist, sie mit etwas wie einem primitiven Int32-Wert zu vergleichen. Der Wert jeder einzelnen Ganzzahl ist unveränderlich - eine 5 ist immer eine 5. Aber wo immer Sie eine Int32 haben, können Sie dort einen anderen Wert setzen.

Für Ihre Domäne bedeutet das, dass Sie eine unveränderliche Adresse VO haben können, aber eine bestimmte Verwendungseinheit kann auf jede Instanz einer Adresse VO verweisen. Dies ermöglicht Korrekturen und andere Änderungen. Sie ändern die einzelnen Felder der Adresse VO nicht - Sie ersetzen sie durch eine ganz neue VO-Instanz.

Als nächstes sollten "Persistenz-IDs" nirgendwo im Domänencode ausgedrückt werden. Sie existieren nur, um die Bedürfnisse der relationalen Datenbanken zu erfüllen, und NoSQL-Datenbanken benötigen sie überhaupt nicht.

Das primäre Telefonszenario sollte ungefähr so ​​aussehen:

%Vor%

Diese Methode kapselt die Idee, eine vorhandene primäre Telefonnummer zu aktualisieren. Die Tatsache, dass Telefonnummern VOs unveränderbar sind, bedeutet, dass Sie einen vorhandenen Wert entfernen und durch einen neuen ersetzen müssen. Was normalerweise am Ende der Datenbank geschieht, insbesondere bei ORMs wie NHibernate, ist das Löschen von SQL und eine nachfolgende Einfügung, um alle Telefonnummern effektiv zu ersetzen. Dies ist in Ordnung, da die ID der VOs keine Rolle spielt.

    
eulerfx 09.12.2012 22:46
quelle
0

Ich würde eine Klasse PhoneNumber erstellen, die die String-Nummer der aktuellen Phone-Klasse enthält und diese als Value-Objekt innerhalb Ihrer Phone-Klasse verwendet:

%Vor%

Später, wenn sich Ihr Code weiterentwickelt, müssen Sie beispielsweise die Telefonnummer überprüfen und in die PhoneNumber-Klasse aufnehmen. Diese Klasse kann dann an verschiedenen Stellen über die gesamte Anwendung wiederverwendet werden.

Die Adresse ist meiner Meinung nach ein Value-Objekt, das Sie wie ein Ganzes behandeln können. Obwohl Sie Street, City, etc ... modellieren könnten, die normalerweise Entitäten sind, aber das ist wahrscheinlich übermodellieren. Kein Teil der Adresse kann sich ändern, das gesamte Objekt wird immer ersetzt, wenn es nach der ersten Erstellung geändert wird.

Die User-Klasse ist innerhalb dieses Beispiels mit diesen Grenzen ein Aggregate-Root (und somit auch eine Entity). Die ContactInfo-Klasse ist kein ValueObject (nicht unveränderbar) und keine Entität (keine echte Identität), sondern ein Aggregat. Es enthält mehrere Klassen, die als Ganzes betrachtet werden sollten.

Weitere Informationen zu Ссылка

Wenn eine Persistenz-ID vorhanden ist, sollten Sie normalerweise an eine Entität denken. Wenn Sie jedoch die Persistenz-IDs hinzufügen möchten, würde ich wie die Phone- und PhoneNumber-Klasse aufteilen. Zum Beispiel Address (Entity mit ID) und AddressValue mit allen anderen Feldern (und Logik über Adresswerte).

Dies sollte auch die Kopfschmerzen bei der Verwaltung der Persistenzidentitäten lösen, da Sie das gesamte Wertobjekt ersetzen und die Persistenzidentität im Fall von updatePrimaryPhoneNumber gleich bleibt.

    
Maarten 10.06.2015 13:04
quelle