it-swarm.com.de

Wie kombiniert man striktes TDD und DDD?

Bei TDD geht es darum, Code zu entwerfen, der von Tests geleitet wird.
Daher werden typische Schichten normalerweise nicht im Voraus aufgebaut. Sie sollten durch Refactoring-Schritte leicht erscheinen.

Das domänengesteuerte Design umfasst viele technische Muster, die gut etablierte Schichten wie Anwendungsschicht, Infrastrukturschicht, Domänenschicht und Persistenzschicht definieren.

Wie verhalte ich mich, um den Codierungsteil eines DDD-Projekts von Grund auf neu zu starten?
Sollte ich das Design strikt aus Tests hervorgehen lassen, dh keine Trennung von Bedenken (keine Schichten) und Refactor, um den technischen Mustern von DDD zu entsprechen?

Oder sollte ich diese leeren Ebenen (Anwendung, Entitäten/Domänendienste, Infrastruktur) erstellen und TDD unabhängig voneinander in jede einzelne Ebene einpassen lassen (mithilfe von Mocks zum Isolieren zwischen Ebenen)?

15
Mik378

Stellen Sie sicher, dass Sie die jüngsten Kommentare von Onkel Bob zur Rolle von Design in TDD lesen.

Das domänengesteuerte Design umfasst viele technische Muster, die gut etablierte Schichten wie Anwendungsschicht, Infrastrukturschicht, Domänenschicht und Persistenzschicht definieren.

Udi Dahan: "Gott, wie ich es hasse, Schichten zu bilden." Er verbringt einige Zeit damit, darüber in seinem Vortrag zu diskutieren CQRS - aber anders (Schichtung beginnt um 18:30 Uhr)

Ich würde Ihren Satz etwas anders buchstabieren; "DDD erkennt an, dass die meisten Geschäftsanwendungen eine Reihe von Problemen gemeinsam haben und dass die Lösungen für diese Probleme unterschiedliche Lebensdauern haben" .

Beispielsweise müssen Domain-Bedenken in der Regel flexibel sein - insbesondere, wenn Sie eine Lösung für ein bestimmtes Unternehmen anpassen. Schließlich geht es in der Domain darum, wie das Unternehmen Geschäfte macht, dh wie das Unternehmen Geld verdient und in der Lage ist, schnell Geschäftsverbesserungen zu erzielen, ist ein freier Umsatz.

Andererseits müssen Sie die Persistenzkomponente wahrscheinlich nicht oft ändern. Die Datenbanklösung, die in der letzten Version funktioniert hat, funktioniert wahrscheinlich auch in dieser Version.

Die Anwendungsprobleme liegen irgendwo in der Mitte; Sie sind in der Regel stabil, sodass die Benutzer nicht bei jeder Veröffentlichung eine neue App lernen müssen.

Es kann auch mehrere Implementierungen geben, um ein bestimmtes Problem zu lösen. Beispielsweise benötigt die Anwendung möglicherweise nur eine Momentaufnahme ihres aktuellen Status. Es reicht aus, nur eine Datei auf der Festplatte zu speichern. Und in Ihren ersten Iterationen ist dies möglicherweise auch alles, was die Domain benötigt. Aber irgendwann kommt eine Geschichte, die Ad-hoc-Abfrageunterstützung erfordert, und Sie erkennen, dass das Konfigurieren einer relationalen Datenbank viel einfacher ist als das Implementieren einer von Grund auf neu. Und dann gibt es noch diese eine Funktion, die in einer Diagrammdatenbank besser funktioniert.

In der Zwischenzeit möchte der CTO eine Version der App, die auf seinem Telefon ausgeführt wird. Der CEO hat gerade von einem Mann gehört, dass die Veröffentlichung einer API die große Sache ist.

Außerdem verwendet das Verkaufsteam ein anderes Modell. Geben Sie uns also dieselbe App mit einem anderen Modell. Oh, aber wir sind viel unterwegs, daher muss unsere Version funktionieren, wenn wir offline sind und später synchronisieren ...

Mit anderen Worten, Sie wenden die taktischen Muster von ddd nicht an, indem Sie leere Platzhalter implementieren und davon ausgehen, dass sie später ausgefüllt werden, sondern indem Sie erkennen, wann Sie die Streams überqueren "Hey, das ist Persistenzcode in meinem Domain-Modell. Ich muss noch nicht mit dem Refactoring fertig sein."

12
VoiceOfUnreason

Test Driven Development (TDD) ist kein Design. Dies ist eine Anforderung, die sich auf Ihr Design auswirkt. Als ob Sie threadsicher sein müssten, ist das kein Design. Auch dies ist eine Anforderung, die sich auf Ihr Design auswirkt.

Wenn Sie alle anderen Designprobleme fröhlich ignorieren und sich religiös an die TDD-Regeln halten, geben Sie TDD keine Schuld, wenn Ihr Code zu Mist wird. Es wird prüfbarer Mist sein, aber es wird Mist sein.

Eine schöne Sache an testbarem Mist ist, dass es sich um einen umgestaltbaren Mist handelt, der für manche Leute gut genug ist. Wir werden nur dann Lust bekommen, wenn es nötig ist. Andere hassen das und beschuldigen TDD dafür. Nein, das ist dein Tun.

Domain Driven Design (DDD) ist etwas, was Sie vor dem rot-grünen Refaktor-Zyklus von TDD tun.

DDD ist das Bemühen, einen Bereich im Code zu erstellen und beizubehalten, in dem ein Domänenexperte, der die Details des Systems weitgehend nicht kennt, verstehen kann, wie das System gesteuert wird. Dies geschieht durch Abstraktion und Modellierung einer Problemdomäne auf vertraute Weise.

Ein DDD-System kann eine Architektur haben, die folgendermaßen aussieht:

(enter image description here

Diese DDD-Architektur hat viele Namen: Clean , Onion , Hexagonal usw.

Hier ist die Trennung, die viele Leute haben, wenn sie sich dieses Design ansehen. Das ist nicht konkret. Ich kann diesem Entwurf folgen und habe noch nie etwas geschrieben, was Sie hier als Diagramm sehen. Ich sehe, dass andere darauf bestehen, dass es ein Anwendungsfallobjekt oder eine Entitätsklasse geben muss. Dies sind Regeln, die Ihnen sagen, mit wem und wie Sie sprechen können.

Das ist es. Befolgen Sie die Regeln dieses Designs und Sie können Ihr kleines Herz heraus TDD. TDD ist es egal, mit wem Sie sprechen. Es ist wichtig, dass alles, was etwas tut, auf Knopfdruck funktioniert oder nicht. Nicht, irgendwo ist etwas kaputt. Es sagt Ihnen genau, was kaputt ist.

Immer noch zu vage? Schauen Sie sich die Controler - Use Case Interactor - Presenter Diagramm in der unteren rechten Ecke. Hier sind drei konkrete Dinge, die miteinander kommunizieren. Sicher, das ist DDD, aber wie fügt man hier TDD hinzu? Verspotten Sie einfach das konkrete Zeug. Der Moderator muss Informationen erhalten. Eine PresenterMock Klasse wäre ein guter Weg, um zu überprüfen, ob sie das bekommt, was Sie erwartet haben. Hand das Use Case Interactor die PresenterMock und fahren die Use Case Interactor als ob du der Controller wärst und du eine gute Möglichkeit hast, den Use Case Interactor da der Mock Ihnen sagen wird, ob er das bekommen hat, was Sie erwartet haben.

Schau dir das an. TDD zufrieden und wir mussten uns nicht mit unserem DDD-Design herumschlagen. Wie ist das passiert? Wir haben mit einem gut entkoppelten Design begonnen.

Wenn Sie TDD verwenden, um das Design voranzutreiben (nicht einfach [~ # ~] d [~ # ~] Entwicklung), erhalten Sie ein Design, das Ihren Aufwand widerspiegelt hineinstecken. Wenn es das ist, was du willst. Aber dafür war TDD nie gedacht. Was am Ende fehlt, ist sicherlich nicht die Schuld von TDD.

Bei TDD geht es nicht um Design. Wenn Sie Designänderungen vornehmen müssen, um TDD zu verwenden, haben Sie größere Probleme als das Testen.

11
candied_orange

TDD stellt sicher, dass in Ihrem Code alle erforderlichen Testfälle parallel zur Entwicklung geschrieben wurden. Dies sollte sich nicht auf das Design auf hoher Ebene auswirken. Denken Sie mehr an die Gräbenarbeit.

Bei DDD dreht sich alles um High-Level-Designs, Sprache zwischen Domain-Experten und Ingenieuren, Kontext-Mapping usw. Dies sollte der Treiber für das High-Level-Design der Anwendung sein.

Dies sind beide flache Erklärungen für zwei leistungsstarke Programmiermethoden. Aber am Ende des Tages erreichen sie wirklich zwei sehr unterschiedliche Dinge.

Beginnen Sie mit der DDD-Sprach- und Kontextzuordnung, und beginnen Sie schließlich mit dem Üben von TDD, wenn Sie den Code schreiben. Die Praxis von TDD sollte sich jedoch nicht auf das Design auf hoher Ebene auswirken, sondern sicherstellen, dass die Dinge getestet werden können. Hier gibt es eine kleine Einschränkung.

Ich denke, es könnte wichtig sein zu beachten: Sie sollten DDD nur üben, wenn die Anwendung komplex genug ist.

4
Matt Oaxaca

[~ # ~] ddd [~ # ~] handelt vom Software-Design.
[~ # ~] tdd [~ # ~] handelt vom Code-Design.

In DDD repräsentiert das "Modell" die Abstraktion der Domäne, das gesamte Wissen des Domänenexperten.

Wir könnten TDD für das Code-Initial-Software-Design-Modell verwenden. Die Domain verfügt über Geschäftsregeln und Domainmodelle, nach denen der Test (erste) grün sein sollte.

Tatsächlich können wir die Tests codieren, nachdem wir ein domänengesteuertes Modell entworfen haben.
Dieses Buch "Wachsende objektorientierte Software, geführt durch Tests"Link-for-Buy
Nehmen Sie diesen Ansatz mit einem wandelndes Skelett , hexagonale Architektur und TDD.

Quelle von: DDD schnell - InfoQ

3
JonyLoscal

Sollte ich Design unbedingt aus Tests hervorgehen lassen?

Nein. (Domain Driven) Design sollte per Definition aus Domain-Anforderungen hervorgehen. Dies ist eine schlechte Idee, alles andere Ihr Design steuern zu lassen, egal ob es sich um eine Testsuite, ein Datenbankschema oder ... handelt (Fortsetzung folgt)

Oder sollte ich diese leeren Ebenen (Anwendung, Entitäten/Domänendienste, Infrastruktur) erstellen und TDD unabhängig in jede dieser Ebenen einpassen lassen

(Fortsetzung) ... oder einige kanonische Schichten von Klassen/Klassenhierarchien in Ihrer bevorzugten OO Sprache Ihrer Wahl, auch wenn es eine sehr ausgereifte und beliebte Sprache ist (schließlich können "Millionen von Fliegen nicht sein" falsch richtig?).

Wenn es um DDD geht, ist OOP nicht ausreichend, um Anforderungen in lesbarer Form auszudrücken, d. H. Etwas, das für einen Nicht-Programmierer mehr oder weniger klar wäre. Streng getippte FP Sprachen machen einen besseren Job. Ich empfehle, ein Buch über DDD mit funktionaler Programmierung "Domain Modeling Made Functional" von Scott Wlaschin zu lesen

https: //pragprog.com/book/swdddf/domain-modeling-made-functional

Sie müssen nicht die Sprache FP verwenden, um einige der Ideen von dort auszuleihen (leider nicht alle), aber wenn Sie sie tatsächlich lesen, werden Sie es wahrscheinlich wollen Verwenden Sie eine funktionale Sprache.

Es wird auch Ihre Frage beantworten, wie TDD in ein DDD-Bild passt. Kurz gesagt, wenn Anforderungen im funktionalen Stil codiert werden, sind keine großen Unit-Tests mehr erforderlich, da die meisten ungültigen Zustände und Szenarien nicht darstellbar/unmöglich zu kompilieren sind. Natürlich gibt es im FP Projekt noch Platz für automatisierte Tests, aber Tests werden keinesfalls zu wichtigen Entwurfsentscheidungen führen.

Um einen vollständigen Kreis zu bilden, kehren wir zur Titelfrage zurück, d. H. "Wie kombiniere ich striktes TDD und DDD?". Die Antwort ist einfach: Es gibt nichts zu kombinieren/keinen Interessenkonflikt. Entwerfen Sie gemäß den Anforderungen, entwickeln Sie gemäß dem Design (indem Sie zuerst Tests schreiben, wenn Sie wirklich TDD machen möchten)

1
KolA