23

TDD is about designing code, guided by tests.
Thus, typical layers aren't usually built upfront; they should slightly appear through refactoring steps.

Domain-driven design involves a lot of technical patterns, defining well established layers like Application layer, Infrastructure layer, Domain Layer, Persistence layer.

To start a DDD project's coding part from scratch, how to behave?
Should I strictly let design emerge from tests, meaning no separation of concerns (no layers) and refactor in order to fit DDD technical patterns?

Or should I create those empty layers (application, entities/domain services, infrastructure) and let TDD fit in each of them independently (using mocks to isolate between layers)?

1

5 Answers 5

16

Make sure you review Uncle Bob's recent comments about the role of design in TDD.

Domain-driven design involves a lot of technical patterns, defining well established layers like Application layer, Infrastructure layer, Domain Layer, Persistence layer.

Udi Dahan: "God, how I hate layering." He spends some time discussing it in his talk CQRS - but Different (layering starts at 18m30s)

I would spell your sentence slightly differently; "DDD recognizes that there are a number of concerns common to most business applications and that the solutions to those concerns have different lifetimes".

For example domain concerns, as a rule, need to be flexible -- especially when you are customizing a solution for a particular business. After all, the domain concerns how the company does business, which is to say, how the company makes money and being able to deliver business improvements quickly is free revenue.

On the other hand, you probably don't need to change the persistence component often. The database solution that worked last release will probably also work this release.

The application concerns are somewhere in the middle; they tend to be stable so that the users don't need to learn a new app with every release.

Also, there can be multiple implementations to solve any given concern. For instance, the application may need only a snapshot of its current state -- simply saving a file to disk will suffice. And in your first few iterations, that may be all the domain needs too. But eventually comes a story that calls for ad-hoc query support, and you recognize that configuring a relational database will be a lot easier than implementing one from scratch. And then there's this one feature that would work better in a graph database.

Meanwhile, the CTO wants a version of the app that runs on his phone; the CEO just heard from a guy that publishing an API is the big thing.

Also, the sales team uses a different model, so give us the same app, with a different model. Oh, but we're travelling a lot, so our version needs to work when we are offline and sync up later...

In other words, you apply the tactical patterns from not by implementing empty placeholders and assuming they will get filled in later, but instead by recognizing when you are crossing the streams "Hey, that's persistence code in my domain model, I must not be done refactoring yet."

0
16

Test Driven Development (TDD) is not a design. It's a requirement that impacts your design. Just as if you were required to be thread safe, that's not a design. Again, it's a requirement that impacts your design.

If you gleefully ignore all other design concerns and religiously keep to the TDD rules don't blame TDD when your code turns into crap. It will be testable crap but it will be crap.

One nice thing about testable crap is that it's refactorable crap so for some people that's good enough. We'll get fancy only when needed. Others hate this and blame TDD for it. No. This is your doing.

Domain Driven Design (DDD) is something you do before TDD's red green refactor cycle.

DDD is the effort to create and preserve a space in the code where a domain expert, who is largely oblivious to the details of the system, can understand how to control the system. This is done by abstraction and modeling a problem domain in a familiar way.

A DDD system can have an architecture that looks like this:

enter image description here

This architecture goes by a lot of names: Clean, Onion, Hexagonal, etc

Here's the disconnect I see many people have when they look at this design. This isn't concrete. I can follow this design and never have written anything you see diagrammed here. I see others insist there must be a use case object or an entities class. What these are is a set of rules that tell you who you can talk to and how.

That's it. Follow the rules of this design and you can TDD your little heart out. TDD doesn't care who you talk to. It cares that everything that does something can be proven to work or not at the click of a button. Not, something somewhere is broken. It tells you exactly what's broken.

Still to vague? Look at the Controler - Use Case Interactor - Presenter diagram in the lower right corner. Here are three concrete things communicating with each other. Sure this is DDD but how do you add TDD here? Just mock the concrete stuff. Presenter must be receiving information. A PresenterMock class would be a good way to check that it's getting what you expected it to get. Hand the Use Case Interactor the PresenterMock and drive the Use Case Interactor as if you were the Controller and you have a nice way to unit test the Use Case Interactor since the mock will tell you if it got what you expected it to get.

Well look at that. TDD satisfied and we didn't have to futz with our DDD design. How did that happen? We started with a well decoupled design.

If you use TDD to drive design (not simply Development) you get a design that reflects the effort you put into it. If that's what you want fine. But that was never what TDD was meant for. What this ends up lacking is certainly not TDD's fault.

TDD is not about design. If you have to make design changes to use TDD you have bigger problems than testing.

17
  • I never said that TDD is a design, but it is about design.
    – Mik378
    May 29, 2016 at 23:34
  • 1
    Uncle Bob was telling you to design. He wasn't telling you "hey if you're tests work who cares about the rest". May 29, 2016 at 23:41
  • 1
    As I already said, just follow the rules of who you're allowed to talk to. The clean architecture isn't something to have a BDUF debate over. Just identify what part you're in and think about who and how you should communicate. It's more agile than you might think. After that ask if it's testable by TDD. If it's not you did something wrong. If it is I hope you were paying attention because good design is more than just testable. May 29, 2016 at 23:52
  • 8
    Ugh... I really can't stand the "Test Driven Design" misnomer. You still have to design a bit before you start blissfully pounding away at your keyboard, whether or not you write tests.
    – RubberDuck
    May 30, 2016 at 0:16
  • 1
    To quote Uncle Bob on this exact point, "You have to DESIGN period". Click there and if you're too impatient to read the whole thing search for those words. You'll find Mr Martin is quite adamant that TDD is no magic bullet and that you must design not only your code but your tests as well if you don't want to live in a very brittle code base. May 30, 2016 at 10:04
7

TDD insures your code has all necessary test cases written in parallel to development. This shouldn't effect high level design. Think of it more in the trenches work.

DDD is all about high level designs, language between domain experts & engineers, context mapping, etc. This should be the driver of the application high level design.

These are both shallow explanations of two powerful programming methodologies. But at the end of the day they really accomplish two very different things.

Start with DDD language & context mapping then eventually when you go to write the code begin the practice of TDD. But the practice of TDD shouldn't affect high level design, but it should insure things can be tested. There's a little bit of a caveat here.

I think it might be important to note: You should only practice DDD if the application is complex enough.

11
  • 3
    I disagree, TDD is not about testing, it's about designing.
    – Mik378
    May 29, 2016 at 18:25
  • I am basing everything off the 3 rules of TDD as described by Uncle Bob. May 29, 2016 at 18:30
  • Steve Freeman, author of GOOS book stated : you shouldn t specify any layers or infrastructure before starting TDD cycles.
    – Mik378
    May 29, 2016 at 18:31
  • I am unfamiliar with that book, but I'd have to disagree. I don't want TDD to shaping my DI & class graph. May 29, 2016 at 18:41
  • 3
    @Mik378: TDD is not about testing, but is also not primarily about designing - the only design induced by TDD is the design for unit-testability. Every other part of the design must come from somewhere else. I see TDD more as an implementation technique.
    – Doc Brown
    May 29, 2016 at 19:37
6

DDD is about software design.
TDD is about code design.

In DDD, the "model" represents de abstraction of domain, all the knowledge from domain expert.

We could use TDD for code initial software design model. The Domain has business rules and domain models that the test written (firsts) should be green.

In effect, we can code the tests, after designing a domain-driven model.
This book "Growing Object-Oriented Software, Guided by Tests" link-for-buy
Take this approach, with a walking skeleton, hexagonal architecture and TDD.

Source from: DDD quickly - InfoQ

3
  • 1
    well to me software and code is the same thing
    – Konrad
    May 20, 2018 at 11:40
  • 1
    It could be the same, too. I tried to say: software as a "solution", "system", "high level" and code as a "implementation", "low level", "details". Oct 26, 2018 at 3:52
  • I think that the important thing is that "first we test, but we need a minimal skeleton where we would start testing". Do you? Oct 26, 2018 at 3:58
4

Should I strictly let design emerge from tests

No. (Domain Driven) Design by definition should emerge from domain requirements. This is bad idea to let anything else to drive your design, whether it is tests suite, database schema or ... (to be continued)

Or should I create those empty layers (application, entities/domain services, infrastructure) and let TDD fit in each of them independently

(continued) ... or some canonical layers of classes/class hierarchies in your favorite OO language of choice, even if it is a very mature and popular one (after all "millions of flies can't be wrong", right?).

When it comes to DDD, OOP falls short expressing requirements in human-readable form i.e. something that would be more or less clear to a non-programmer. Strictly typed FP languages do a better job. I recommend to read a book about DDD using functional programming "Domain Modeling Made Functional" by Scott Wlaschin

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

You don't have to use FP language to borrow some of the ideas from there (not all of them unfortunately), but if you actually read it then you probably will want to use a functional language.

It will also answer your question about how TDD fits in DDD picture. In a nutshell, when requirements are coded in functional style it eliminates the need for vast amount of unit tests as it makes most of invalid states and scenarios unrepresentable / impossible to compile. Of course, there is still place for automated testing in FP project but by no means tests will drive any major design decisions.

To make a full circle let's return to the title question i.e. "How to combine strict TDD and DDD?". The answer is straightforward: there is nothing to combine / no conflict of interest. Design according to requirements, develop according to design (by writing tests first if you really want to do TDD)

1
  • 2
    +1 for FP DDD book and overall explanation.
    – Roman Susi
    Aug 4, 2019 at 18:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.