Wie kombiniere ich strikte TDD und DDD?

15

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

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

Wie verhält man sich, 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 Refaktor, um es an die technischen Muster von DDD anzupassen?

Oder sollte ich diese leeren Ebenen (Anwendung, Entitäten / Domänendienste, Infrastruktur) erstellen und TDD unabhängig voneinander in jede Ebene einpassen lassen (indem ich Mocks verwende, um zwischen den Ebenen zu isolieren)?

Mik378
quelle
3
Verwandte: Führt TDD zum guten Design?
Doc Brown

Antworten:

11

Lesen Sie unbedingt die jüngsten Kommentare von Onkel Bob zur Rolle des Designs in TDD .

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

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

Ich würde Ihren Satz etwas anders buchstabieren; „DDD erkennt , dass es eine Reihe von Bedenken in den meisten Geschäftsanwendungen ist und dass die Lösungen auf diese Bedenken haben unterschiedliche Lebenszeiten“ .

Beispielsweise müssen Domain-Belange in der Regel flexibel sein - insbesondere, wenn Sie eine Lösung für ein bestimmtes Unternehmen anpassen. Schließlich geht es bei der Domäne darum, wie das Unternehmen Geschäfte tätigt, dh wie das Unternehmen Geld verdient und in der Lage ist, schnell Geschäftsverbesserungen zu erzielen, und dies sind die freien Einnahmen.

Andererseits müssen Sie die Persistenzkomponente wahrscheinlich nicht häufig ändern. Die Datenbanklösung, die im letzten Release funktioniert hat, funktioniert wahrscheinlich auch in diesem Release.

Die Anwendungsprobleme liegen irgendwo in der Mitte. Sie sind in der Regel stabil, sodass die Benutzer nicht mit jeder Version eine neue App erlernen müssen.

Es kann auch mehrere Implementierungen geben, um ein bestimmtes Problem zu lösen. Beispielsweise benötigt die Anwendung möglicherweise nur einen Schnappschuss ihres aktuellen Status - es reicht aus, eine Datei auf der Festplatte zu speichern. Und in den ersten paar Iterationen ist dies möglicherweise auch alles, was die Domäne benötigt. Aber irgendwann kommt eine Geschichte, die Unterstützung für Ad-hoc-Abfragen erfordert, und Sie erkennen, dass die Konfiguration einer relationalen Datenbank viel einfacher ist als die Implementierung einer neuen Datenbank. Und dann gibt es noch eine Funktion, die in einer Grafikdatenbank besser funktioniert.

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

Außerdem verwendet das Verkaufsteam ein anderes Modell. Geben Sie uns daher 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, wenden Sie die taktischen Muster von nicht durch leere Platzhalter Implementierung und unter der Annahme , werden sie in später gefüllt werden, sondern durch die Anerkennung , wenn Sie die Bäche überqueren „Hey, dass Beharrlichkeit Code in meinem Domain - Modell, ich muss nicht sein Refactoring noch nicht abgeschlossen. "

VoiceOfUnreason
quelle
11

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

Wenn Sie fröhlich alle anderen Designprobleme ignorieren und die TDD-Regeln religiös einhalten, machen Sie TDD nicht dafür verantwortlich, wenn sich Ihr Code in Mist verwandelt. Es wird testbarer Mist sein, aber es wird Mist sein.

Eine nette Sache an testbarem Mist ist, dass es refactorable Mist ist, also für einige Leute, die gut genug sind. Wir werden nur dann Lust bekommen, wenn es nötig ist. Andere hassen das und machen TDD dafür verantwortlich. Nein, das machst du.

Domain Driven Design (DDD) wird vor dem Rot-Grün-Refactor-Zyklus von TDD durchgeführt.

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 bekannte Weise.

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

Bildbeschreibung hier eingeben

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

Ist hier die Trennung, die ich sehe, dass viele Leute haben, wenn sie diesen Entwurf betrachten. Das ist nicht konkret. Ich kann diesem Entwurf folgen und habe noch nie etwas geschrieben, das Sie hier in einem Diagramm sehen. Ich sehe, dass andere darauf bestehen, dass es ein Use-Case-Objekt oder eine Entitätsklasse geben muss. Was dies sind, ist ein Satz von Regeln, die Ihnen sagen, mit wem Sie sprechen können und wie.

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

Immer noch zu vage? Schauen Sie sich das Controler- Use Case Interactor- PresenterDiagramm in der unteren rechten Ecke an. 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 PresenterMockKlasse wäre ein guter Weg, um zu überprüfen, ob sie das bekommt, was Sie erwartet haben. Hand , um die Use Case Interactordas PresenterMockund fahren die , Use Case Interactorals ob Sie die waren , Controllerund Sie haben eine schöne Art und Weise zu Unit - Test der Use Case Interactorseit der mock wird Ihnen sagen , wenn es bekommen , was Sie erwartet , es zu bekommen.

Na sieh dir das an. TDD zufrieden und wir mussten mit unserem DDD-Design nicht viel anfangen. Wie ist das passiert? Wir haben mit einem gut entkoppelten Design begonnen.

Wenn Sie TDD verwenden Design zu fahren (nicht nur D evelopment) Sie ein Design erhalten, die den Aufwand reflektiert man hineinsteckt. Wenn es das ist, was du willst, gut. Aber dafür war TDD nie gedacht. Was dabei letztendlich fehlt, ist sicherlich nicht TDDs Schuld.

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.

kandierte_orange
quelle
Ich habe nie gesagt, dass TDD ein Design ist, aber es geht um Design.
Mik378
1
Onkel Bob hat dir gesagt, du sollst entwerfen. Er sagte dir nicht "Hey, wenn du Testarbeiten machst, kümmert dich der Rest".
candied_orange
1
Wie ich bereits sagte, befolge einfach die Regeln, mit wem du reden darfst. Die saubere Architektur ist kein Anlass für eine BDUF-Debatte. Identifizieren Sie einfach, in welchem ​​Teil Sie sich befinden, und überlegen Sie, wer und wie Sie kommunizieren sollten. Es ist agiler als Sie vielleicht denken. Fragen Sie danach, ob es von TDD getestet werden kann. Wenn es nicht so ist, hast du etwas falsch gemacht. Ich hoffe, Sie haben aufgepasst, denn gutes Design ist mehr als nur prüfbar.
candied_orange
6
Ugh ... Ich kann die Fehlbezeichnung "Test Driven Design" wirklich nicht ausstehen. Sie müssen noch ein wenig entwerfen, bevor Sie selig auf Ihrer Tastatur herumstampfen, egal ob Sie Tests schreiben oder nicht.
RubberDuck
1
Um Onkel Bob genau zu diesem Punkt zu zitieren: "Sie müssen Periode DESIGNEN" . Klicken Sie dort und wenn Sie zu ungeduldig sind, um das Ganze zu lesen, suchen Sie nach diesen Wörtern. Sie werden feststellen, dass Herr Martin fest davon überzeugt ist, dass TDD kein Wundermittel ist und dass Sie nicht nur Ihren Code, sondern auch Ihre Tests entwerfen müssen, wenn Sie nicht in einer sehr spröden Codebasis leben möchten.
candied_orange
4

TDD versichert, dass Ihr Code über alle notwendigen Testfälle verfügt, die parallel zur Entwicklung geschrieben wurden. Dies sollte keine Auswirkungen auf das High-Level-Design haben. Denken Sie daran, mehr in den Gräben zu arbeiten.

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

Dies sind beide flache Erklärungen zweier leistungsfähiger 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. Das Üben von TDD sollte sich jedoch nicht auf das High-Level-Design auswirken, sondern sicherstellen, dass die Dinge getestet werden können. Hier gibt es eine kleine Einschränkung.

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

Matt Oaxaca
quelle
1
Ich stimme nicht zu, bei TDD geht es nicht um Testen, sondern um Entwerfen.
Mik378
Ich stütze alles auf die 3 Regeln von TDD, wie sie von Onkel Bob beschrieben wurden.
Matt Oaxaca
Steve Freeman, Autor des GOOS-Buches, erklärte: Sie sollten keine Layer oder Infrastruktur angeben, bevor Sie TDD-Zyklen starten.
Mik378
Ich bin mit diesem Buch nicht vertraut, müsste aber anderer Meinung sein. Ich möchte nicht, dass TDD mein DI- und Klassendiagramm formt.
Matt Oaxaca
3
@ Mik378: Bei TDD geht es nicht um das Testen, sondern auch nicht primär um das Entwerfen - das einzige von TDD hervorgerufene Design ist das Design für die Testbarkeit von Einheiten. Jeder andere Teil des Entwurfs muss von woanders stammen. Ich sehe TDD eher als Implementierungstechnik.
Doc Brown
3

Bei DDD geht es um Software-Design.
Bei TDD geht es um Code-Design.

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

Wir könnten TDD für das erste Code-Software-Design-Modell verwenden. Die Domäne verfügt über Geschäftsregeln und Domänenmodelle, für die der geschriebene Test (Firsts) grün sein sollte.

Tatsächlich können wir die Tests nach dem Entwerfen eines domänengetriebenen Modells codieren.
Dieses Buch "Wachsende objektorientierte Software, geführt von Tests" link-for-buy
Nehmen Sie diesen Ansatz mit einem Laufgerüst , einer hexagonalen Architektur und TDD.

Quelle: DDD schnell - InfoQ

JonyLoscal
quelle
1
Nun, für mich ist Software und Code dasselbe
Konrad
1
Das könnte auch so sein. Ich habe versucht zu sagen: Software als "Lösung", "System", "High Level" und Code als "Implementierung", "Low Level", "Details".
JonyLoscal
Ich denke, das Wichtigste ist, dass "wir zuerst testen, aber wir brauchen ein minimales Skelett, in dem wir mit dem Testen beginnen würden". Machst du?
JonyLoscal
1

Sollte ich Design aus Tests unbedingt entstehen lassen?

Nein. (Domain Driven) Design sollte per Definition aus Domain-Anforderungen hervorgehen. Dies ist eine schlechte Idee, alles andere als das, was Ihr Design antreibt, zuzulassen, ob es sich um eine Testsuite, ein Datenbankschema oder ... handelt (wird fortgesetzt).

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, auch wenn es eine sehr ausgereifte und beliebte ist (schließlich können "Millionen Fliegen nicht falsch sein", oder?) .

Wenn es um DDD geht, werden die Anforderungen von OOP in einer für den Menschen lesbaren Form ausgedrückt, die für einen Nicht-Programmierer mehr oder weniger klar wäre. Streng getippte FP-Sprachen machen es besser. Ich empfehle, ein Buch über DDD mit der funktionalen Programmierung "Domain Modeling Made Functional" von Scott Wlaschin zu lesen

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

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

Es beantwortet auch Ihre Frage, wie TDD in ein DDD-Bild passt. Kurz gesagt, wenn Anforderungen im funktionalen Stil codiert werden, müssen nicht mehr viele Komponententests durchgeführt werden, 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 die Tests werden keinesfalls zu größeren Entwurfsentscheidungen führen.

Um einen vollständigen Kreis zu bilden, kehren wir zur Titelfrage zurück, dh "Wie werden strikte TDD und DDD kombiniert?". Die Antwort ist einfach: Es gibt nichts zu kombinieren / keinen Interessenkonflikt. Entwerfen Sie nach Anforderungen, entwickeln Sie nach Design (indem Sie zuerst Tests schreiben, wenn Sie wirklich TDD machen möchten)

KolA
quelle
+1 für FP DDD-Buch und allgemeine Erklärung.
Roman Susi