How to Combine Strict TDD and DDD

Architecturedomain-driven-designtddtesting

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)?

Best Answer

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."