Best Practices für die Serialisierung von DDD-Aggregaten

23

Laut DDD sollte die Domänenlogik nicht mit technischen Problemen wie Serialisierung, objektrelationaler Zuordnung usw. verschmutzt werden.

Wie können Sie also den Aggregatzustand serialisieren oder zuordnen, ohne ihn öffentlich über Getter und Setter verfügbar zu machen? Ich habe viele Beispiele für z. B. Repository-Implementierungen gesehen, aber praktisch alle stützten sich bei der Zuordnung auf öffentliche Zugriffsmethoden für die Entitäten und Wertobjekte.

Wir könnten Reflektion verwenden, um öffentliche Zugriffsmethoden zu vermeiden, aber IMO wären diese Domänenobjekte implizit immer noch vom Serialisierungsproblem abhängig. Sie konnten z. B. ein privates Feld nicht umbenennen oder entfernen, ohne Ihre Serialisierungs- / Zuordnungskonfiguration zu optimieren. Sie müssen also eine Serialisierung in Betracht ziehen, bei der Sie sich stattdessen auf die Domänenlogik konzentrieren sollten.

Also, was ist der beste Kompromiss, um hier zu folgen? Lebe mit öffentlichen Accessoren, aber vermeide es, sie für etwas anderes als das Zuordnen von Code zu verwenden? Oder habe ich nur etwas Offensichtliches verpasst?

Ich bin ausdrücklich daran interessiert, den Status von DDD-Domänenobjekten (Aggregate, die aus Entitäten und Wertobjekten bestehen) zu serialisieren. Hier geht es NICHT um die Serialisierung in allgemeinen oder Transkationsskriptszenarien , in denen zustandslose Dienste für einfache Datencontainerobjekte ausgeführt werden.

EagleBeak
quelle

Antworten:

12

Arten von Objekten

Lassen Sie uns zu Zwecken unserer Diskussion unsere Objekte in drei verschiedene Arten unterteilen:

Geschäftsbereichslogik

Dies sind die Objekte, die die Arbeit erledigen. Sie transferieren Geld von einem Girokonto auf ein anderes, erfüllen Aufträge und alle anderen Aktionen , die von Unternehmenssoftware erwartet werden.

Domänenlogikobjekte erfordern normalerweise keine Accessoren (Getter und Setter). Stattdessen erstellen Sie das Objekt, indem Sie die Abhängigkeiten über einen Konstruktor übergeben und das Objekt dann über Methoden bearbeiten (Tell, Don't Ask).

Datenübertragungsobjekte

Datenübertragungsobjekte sind reine Zustände. Sie enthalten keine Geschäftslogik. Sie werden immer Accessoren haben. Sie können Setter haben oder auch nicht, je nachdem, ob Sie sie unveränderlich schreiben oder nicht . Sie werden entweder Ihre Felder im Konstruktor festlegen und deren Werte werden sich während der Lebensdauer des Objekts nicht ändern, oder Ihre Zugriffsmethoden werden gelesen / geschrieben. In der Praxis sind diese Objekte normalerweise veränderbar, sodass ein Benutzer sie bearbeiten kann.

Modellobjekte anzeigen

Ansichtsmodellobjekte enthalten eine anzeigbare / bearbeitbare Datendarstellung. Sie können Geschäftslogik enthalten, die normalerweise auf die Datenüberprüfung beschränkt ist. Ein Beispiel für ein View Model-Objekt kann ein InvoiceViewModel sein, das ein Customer-Objekt, ein Invoice Header-Objekt und Rechnungsposten enthält. View Model-Objekte enthalten immer Accessoren.

Die einzige Art von Objekt, die in dem Sinne "rein" ist, dass sie keine Feldzugriffsmethoden enthält, ist das Domain Logic-Objekt. Durch die Serialisierung eines solchen Objekts wird sein aktueller "Berechnungsstatus" gespeichert, sodass er später abgerufen werden kann, um die Verarbeitung abzuschließen. Ansichtsmodelle und DTOs können frei serialisiert werden, aber in der Praxis werden ihre Daten normalerweise in einer Datenbank gespeichert.

Serialisierung, Abhängigkeiten und Kopplung

Zwar entstehen durch die Serialisierung Abhängigkeiten in dem Sinne, dass Sie eine Deserialisierung für ein kompatibles Objekt durchführen müssen, doch müssen Sie die Serialisierungskonfiguration nicht unbedingt ändern. Gute Serialisierungsmechanismen sind allgemeine Zwecke. Es ist ihnen egal, ob Sie den Namen einer Eigenschaft oder eines Mitglieds ändern, solange sie den Mitgliedern noch Werte zuordnen können. In der Praxis bedeutet dies nur, dass Sie die Objektinstanz erneut serialisieren müssen, um die Serialisierungsdarstellung (xml, json usw.) mit Ihrem neuen Objekt kompatibel zu machen. Es sollten keine Konfigurationsänderungen am Serializer erforderlich sein.

Es ist richtig, dass sich Objekte nicht damit befassen sollten, wie sie serialisiert werden. Sie haben bereits eine Möglichkeit beschrieben, wie solche Bedenken von den Domänenklassen abgekoppelt werden können: Reflexion. Der Serialisierer sollte sich jedoch Gedanken darüber machen, wie er Objekte serialisiert und deserialisiert. das ist schließlich seine Funktion. Sie können Ihre Objekte vom Serialisierungsprozess entkoppeln, indem Sie die Serialisierung zu einer Allzweckfunktion machen , die für alle Objekttypen geeignet ist .

Verwirrung stiftet unter anderem, dass die Entkopplung in beide Richtungen erfolgen muss. Es tut nicht; es muss nur in eine richtung funktionieren . In der Praxis kann man sich niemals vollständig entkoppeln. es gibt immer eine Kopplung. Das Ziel der losen Kopplung besteht darin, die Pflege des Codes zu vereinfachen und nicht alle Abhängigkeiten zu beseitigen.

Robert Harvey
quelle
Ich stimme Ihrer Auffassung zur Entkopplung zu. Der Serializer hängt vom Domänenobjekt ab und das ist in Ordnung. Aber nicht umgekehrt. Ich bin jedoch nicht einverstanden mit Ihrer Ansicht zu öffentlichen Zugriffsmethoden für Domänenobjekte. In der Praxis haben sie sie oft, ja. Aber IMO wäre es vorzuziehen, die Domänenlogik in einem sauberen objektorientierten Design zu implementieren: Sagen Sie, fragen Sie nicht . Dennoch benötigen Sie Accessoren für Zuordnungszwecke (ORM, Serialisierung, GUI ...). Und das möchte ich nach Möglichkeit vermeiden.
EagleBeak
Wie planen Sie, auf Ihre Felder zuzugreifen, wenn Sie keine Accessoren haben?
Robert Harvey
Eigentlich beziehe ich mich auf keine der drei Arten von Objekten, die Sie beschreiben, sondern auf "Aggregate" in der DDD-Terminologie und ihren Unterobjekten (Einheiten, Wertobjekte). Mir ist jetzt klar, dass meine Frage dazu nicht explizit genug war. Es tut uns leid! Bitte beachten Sie meine Bearbeitung oben.
EagleBeak
1
Dies ist im Grunde genommen ein ungelöstes Problem. Sie können nicht gleichzeitig Kapselung, Entkopplung und Serialisierung verwenden. Die Kodierung von DTO ist ein Weg, um einen Kompromiss zu erzielen. Es gibt jedoch viel weniger aufdringliche Möglichkeiten: yegor256.com/2016/07/06/data-transfer-object.html
Basilevs
1
Dadurch wird die Kapselung aufgehoben. Jeder kann die Klasse friend implementieren oder verwenden, um die Interna des Objekts zu lesen.
Basilevs
-1

Der eigentliche Zweck der Serialisierung besteht darin, sicherzustellen, dass die von einem System erzeugten Daten von einem oder mehreren kompatiblen Systemen verarbeitet werden können.

Der einfachste und robusteste Ansatz für die Serialisierung besteht darin, die Daten in ein typunabhängiges Format zu übersetzen, das die Struktur in einem einfachen und benutzerfreundlichen Format beibehält. Beispielsweise verwenden die am weitesten verbreiteten Serialisierungsformate (z. B. JSON, XML) ein genau definiertes textbasiertes Format. Text ist einfach zu produzieren, zu übertragen und zu konsumieren.

Es gibt zwei Gründe, warum die Verwendung eines dieser Formate möglicherweise nicht ideal ist.

  1. Effizienz

    Die Übersetzung aller Daten in ihre textbasierten Entsprechungen ist mit Kosten verbunden. Datentypen gäbe es nicht, wenn Text die effizienteste Möglichkeit wäre, die verschiedenen Datenformen auszudrücken. Darüber hinaus ist die Struktur dieser Formate nicht ideal, um Teilmengen von Daten asynchron oder in Teilen abzurufen.

    XML und JSON gehen beispielsweise davon aus, dass die verwendeten Daten von Anfang bis Ende geschrieben und gelesen werden. Für die Verarbeitung sehr großer Datenmengen, in denen der Speicher knapp ist, kann es erforderlich sein, dass das System, das die Daten verbraucht, die Daten in Teilen verarbeiten kann. In diesem Fall ist möglicherweise eine spezielle Serialisierungs- / Deserialisierungsimplementierung erforderlich, um die Daten zu verarbeiten.

  2. Präzision

    Das Casting, das zum Serialisieren / Deserialisieren der Daten vom beabsichtigten Typ zum datenunabhängigen Typ erforderlich ist, führt zu einem Genauigkeitsverlust.

Man könnte argumentieren, dass das Erzeugen einer binären Darstellung der Objekte und Daten eindeutig die effizienteste und genaueste Lösung ist. Der Hauptnachteil ist, dass die Implementierung aller Systeme, die Daten verbrauchen und produzieren, kompatibel bleiben muss. In der Theorie ist dies eine einfache Einschränkung, in der Praxis jedoch ein Albtraum, da sich Produktionssysteme im Laufe der Zeit ändern bzw. weiterentwickeln.

Nachdem das gesagt worden ist. Das Entkoppeln der Serialisierung / Deserialisierung von den domänenspezifischen Details ist in der Regel sinnvoll, da Allzweckformate robuster sind, über verschiedene Systeme hinweg besser unterstützt werden und nur einen geringen bis keinen erhöhten Wartungsaufwand erfordern.

Evan Scholle
quelle
Sorry, aber das beantwortet meine Frage nicht. Es geht darum, Domänenobjekte von der Serialisierung zu entkoppeln, nicht um die Gründe für die Serialisierung oder die Vor- und Nachteile verschiedener Formate. Wie serialisiere ich Domänenobjekte, ohne ihren privaten Status öffentlich zu machen?
EagleBeak
@EagleBeak Oh, ich wusste nicht, dass es Ihnen speziell um den Umgang mit privaten Mitgliedern geht. In Ihrem Fall können Sie eine binäre Serialisierung durchführen (vorausgesetzt, das empfangende System folgt denselben Regeln / Strukturen, unter denen die Domänenobjekte erstellt wurden) oder eine Logik schreiben, die nur die öffentlichen Daten vor der Serialisierung extrahiert.
Evan Plaice
Ich denke, die "übliche" Annahme ist, dass die Daten, die in ein Universalformat (z. B. XML, JSON) serialisiert werden, öffentlich sind und dass die Berechtigungen über die API über ACLs oder eine andere Entsprechung gesteuert werden. Die Universal-Serialisierung / Deserialisierung erfolgt eher im Sinne einer Entkopplung von Daten von der Geschäftslogik von einem System zum anderen.
Evan Plaice
Ich bin damit einverstanden, dass öffentliche Accessoren normalerweise für zu serialisierende Objekte angenommen werden. Aber ich würde immer noch gerne mehr darüber erfahren, wie dies mit DDD und seiner starken Konzentration auf die Verkapselung von Domänenlogik zusammenhängt. Machen alle DDD-Anwender den Status des Domain-Modells lediglich über öffentliche Zugriffsmethoden für die Serialisierung zugänglich (und erwähnen ihn in ihren Beispielen nie)? Ich bezweifle das. Bitte versteh mich nicht falsch. Ich schätze Ihre Eingabe sehr. Es ist nur so, dass ich mich für einen anderen Aspekt interessiere. (Bisher denke ich nicht, dass meine Frage zu vage ist, aber Robert Harveys Antwort und Ihre haben mich dazu gebracht, darüber nachzudenken.)
EagleBeak