Dürfen Domain-Objekte in Domain Driven Design nur schreibgeschützt sein?

13

Ich lese seit fast zwei Jahren über domänengetriebenes Design und führe einige Konzepte vorsichtig in meine tägliche Arbeit ein oder erstelle zumindest Pläne dafür, wie Dinge, die ich regelmäßig in einem domänengetriebenen Design mache, getan werden können.

Eine Schlussfolgerung, zu der ich zu kommen beginne, insbesondere nachdem ich mehr über Event Sourcing und Command Query Responsibility Segregation (CQRS) gelesen habe, dass Domänenobjekte möglicherweise nur für Schreibzwecke verwendet werden sollen. Um es klarer zu machen, scheint es so zu sein, als ob die Leute in einem Großteil der Dokumentation, die ich gelesen habe, subtil vorschlagen, dass die Domänenobjekte für domänenzentrierte Operationen / Berechnungen und Überprüfungen verantwortlich sind und dann hauptsächlich dazu dienen, einen Weg zum Durchhalten zu finden die Infrastruktur, die in einer Repository-Implementierung bereitgestellt wird. Obwohl mir die Tatsache gefällt, dass dies das Domänenmodell erheblich vereinfachen kann, da es die Verantwortung für die Offenlegung des Status ausschließt.

Wenn es in der Tat richtig ist, dass Domänenobjekte hauptsächlich als schreibgeschützte Objekte verwendet werden sollen, wirft dies einige Fragen für mich auf, auf die ich hoffe, dass jemand antworten kann.

  1. Wie führt man Komponententests für ein Objekt durch, das Setter enthält, oder für Methoden, die den Status eines Objekts ändern, aber keine nach außen hin öffentliche Schnittstelle zum Lesen des Status bereitstellen, z. B. Eigenschafts-Getters in C #? Ist es in Ordnung, den Status nur zu dem Zweck offenzulegen, das Objekt testbar zu machen?
  2. Wie kann man einem Benutzer die Ergebnisse von Berechnungen oder Operationen anzeigen, die in der Domäne ausgeführt wurden, ohne dass diese beibehalten werden müssen, und dann die Ergebnisse aus dem permanenten Speicher außerhalb des Domänenkontexts abrufen? Ist es in Ordnung, den Status nur zum Zwecke der Ergebnisdarstellung freizugeben?

Ist die Faustregel, dass die einzigen Eigenschaften, die Zugriff erhalten, diejenigen sein sollten, die auch in der Domäne beschreibbar sind? Oder anders gesagt: Nur readonly-Eigenschaften sollten vermieden werden, da sie nur zu Lesezwecken vorhanden sind und somit im eigentlichen Domain-Modell keine notwendige Rolle spielen?

Zugehöriges Material:

  1. TDD, DDD und Encapsulation
jpierson
quelle

Antworten:

9

Ich bin mir nicht sicher, ob es eine "Ein-Weg-Antwort" für einen Designansatz gibt, der sich, um fair zu sein, noch weiterentwickelt. Erstens sind DDD und CQRS nicht dasselbe, obwohl die CQRS-Leute anscheinend von einem DDD-beeinflussten Ausgangspunkt abgeleitet sind.

In der DDD-Denkweise ist viel los und vieles hat mit genau definierten Grenzen der Probleme, der Kommunikation zwischen den Beteiligten und der Interaktion zwischen Systemen zu tun, nicht unbedingt mit einer spezifischen Implementierung im Code. Kern ist eine Tugend.

Vielleicht sehen Sie einen Teil der Debatte darüber, ob und wie Domänenobjekte änderbar sein sollten und welche Funktion ein Domänenobjekt in einem Gesamtsystem hat. CQRS unterteilt ein System in Lese- und Schreibpfade. Es ist also sinnvoll zu folgern, dass Sie keinen Lesezugriff benötigen, wenn Sie sich auf dem Schreibpfad befinden. Das Lesen wird dann zu etwas, das Sie gegen die Ereignisse tun, die von einigen Domänenobjekten ausgelöst und von anderen verarbeitet werden. Wenn Sie ein wenig in den CQRS-Verlauf zurückgehen, werden Sie Argumente finden, die besagen, dass Domänenobjekte keine Setter, nur Getters und eine einzige 'Handler'-Methode haben sollten. Die Logik hier ist, dass nur das Verbrauchen von Ereignissen zu einer Zustandsänderung führen sollte und diese Änderung vollständig intern vom Domänenobjekt behandelt wird.

Sie zeigen die Ergebnisse der Änderung, indem Sie sie als separate Änderungsartefakte behandeln, sie in eine separate, flache, beständige Struktur (z. B. eine Tabelle) einfügen und sie lesen, als würden Sie nur einen Bericht über den aktuellen und den historischen Status des Systems lesen . Zum Beispiel könnten Sie ein Ereignis konsumieren, indem Sie die Daten, die Sie lesen müssen, extrahieren und in einer Datenbanktabelle speichern, die einer einzelnen Ansicht (z. B. einem Bildschirm) Ihres Systems genau zugeordnet ist.

Wenn Sie mit diesem Stil experimentieren, müssen Sie sich darüber im Klaren sein, dass andere Programmierer mit diesem Ansatz wahrscheinlich nicht vertraut sind und dass es relativ wenige (aber interessante) Szenarien gibt, in denen dies als Entwurfsansatz gerechtfertigt ist.

Für Unit-Tests gibt es einige Ansätze, die für Sie funktionieren könnten. Die erste und natürlichste Möglichkeit besteht darin, zu überprüfen, ob die von Ihren Domänenobjekten erwarteten Ereignisse korrekt sind. Ein Domänenobjekt kann ein generisches geändertes Ereignis auslösen, das Informationen über die Änderung enthält. Sie könnten das überprüfen. Sie können überprüfen, ob das Ereignis überhaupt ausgelöst wurde und ob andere Ereignisse nicht ausgelöst wurden.

Ein anderer Ansatz ist die Verwendung von Testspionen, die lesbare Attribute für Ihr Domänenobjekt verfügbar machen, damit Sie Statusänderungen überprüfen können. Sie können beispielsweise von Ihrem Domänenobjekt erben, einige Accessoren hinzufügen, um den Zustand zu lesen, der sonst eingekapselt wäre, und überprüfen, ob er korrekt ist.

Auch hier sind Sie verwirrt, weil diese Ansätze verwirrend sind. Wenn Sie einige gute Ideen in Ihre Programmierung übernehmen möchten, nehmen Sie zuerst die saftigen Teile. DDD-Grenzen und explizite Rollen sind Änderungen Ihrer Denkweise bei der Kommunikation mit Ihrem Code. Mindestens CQRS deutet darauf hin, dass das Lesen und Schreiben von Daten voneinander trennbare Vorgänge sind. Diese Einsicht lässt Sie sehr klar darüber nachdenken, welche Rolle die Daten spielen, die Sie präsentieren müssen, wie viel Sie wirklich benötigen, wer sie verbraucht, wie frisch sie sein müssen usw. Sie brauchen keine Umfassende Implementierung von Event Sourcing für eine bessere Kapselung Ihrer Codierung. Sie können beginnen, indem Sie sich nur auf atomare Operationen in Ihrem Objekt konzentrieren. "Tell, Don't Ask" -Ansätze für das Design von Objektschnittstellen.

pfries
quelle
1
+1 für den Start mit den Jucy Bits. Außerdem: Nur das CQS abzubeißen (den 'Events'-Teil vorerst auszulassen) könnte ein guter Anfang sein.
Cottsak
1

Dürfen Domain-Objekte in Domain Driven Design nur schreibgeschützt sein?

Nr CQRS können neben DDD verwendet.

NimChimpsky
quelle
Aber dreht sich bei der Abfrage in CQRS alles um das Abfragen von Daten, die vom Domänenmodell zum Schreiben von Änderungen am Modell verwendet werden, oder kann sie zum Abfragen von Daten für die Anwendungsschicht verwendet werden, die dem Benutzer möglicherweise die Werte anzeigen? Ich höre von einigen, dass es bei DDD nur darum geht, Änderungen zu koordinieren, und dass das Lesen nur dazu verwendet werden sollte, um Änderungen an einem anderen Objekt innerhalb des Domänenmodells zu koordinieren. Die Entscheidung auf die eine oder die andere Weise würde ein radikal anderes Modelldesign bedeuten, da exponierte Daten auf Domänenobjekten in begrenztem Umfang variieren würden, wenn sie nur in der Domäne verwendet würden.
jpierson
0

Bei den Unit-Tests Ihres Domänenmodells sollte überprüft werden, ob für jeden ausgeführten Befehl die richtigen Domänenereignisse ausgelöst werden. Ihre Domänenbefehle und erfassten Ereignisse können nach dem Status abgefragt werden.

Sie können auch ToString()Ihre Domänenbefehle und -ereignisse überschreiben , um eine automatische, lesbare Statusmeldung zu erhalten.

Um Ihre zweite Frage zu beantworten und die Ergebnisse von Befehlen anzuzeigen, sollten Sie dafür sorgen, dass Domänenereignisse in Ihrem Lesemodell "veröffentlicht" werden.

Ed James
quelle
Könnten Sie etwas näher erläutern, ob dies auch dann noch zutrifft, wenn Sie kein Event-Sourcing einsetzen?
Jpierson
1
Ohne Event-Sourcing funktioniert mein Vorschlag möglicherweise nicht. Ich kenne die Wege Ihres Codes nicht. Für ein Domänenobjekt, das im Idealfall keine Eigenschaften verfügbar macht, können Sie möglicherweise explizit eine Testschnittstelle implementieren, die die Eigenschaften bereitstellt, die Sie testen möchten. Nur Ihr Testcode kann solche Domänenobjekte in die Testschnittstelle "umwandeln".
Ed James
Danke für diesen Vorschlag. Ich bin ein wenig unzufrieden mit der Idee, meine Domänenklassen speziell für die Testbarkeit zu ändern, aber ich nehme an, dass dies eine dieser Grauzonen ist, die noch in Domain Driven Design enthalten sind, wenn Sie möchten, dass sie testbar sind. Ein anderer Gedanke ist, dass, wenn man Stabilisierbarkeit und Testbarkeit über dieselbe Schnittstelle verfügbar macht, zumindest nur eine Infrastrukturabhängigkeit anstelle von zwei eingeführt wird. Was denken andere darüber?
jpierson