There’s one central guideline that unites Unit Testing, Domain-Driven Design (and Functional Programming, for that matter):

Keeping your domain model isolated from the external world.

You can’t build a rich domain model if that domain model is responsible for not only the domain logic itself, but also for communicating with out-of-process dependencies.

The reason why is the Separation of Concerns principle.

Domain modeling and communicating with out-of-process dependencies are two distinct responsibilities. Attributing them both to your domain model makes that domain model overcomplicated, and unnecessarily so.

Your application’s domain logic is complex enough already. Mixing it together with persistence concerns (or other out-of-process dependencies) makes it much harder to reason about.

Here’s a nice formula to visualize the Separation of Concerns principle:

[Enable Images]

The complexity of two concerns combined is always much higher than when you tackle these concerns separately.

That’s the DDD perspective on the domain model isolation. Interestingly enough, proper unit testing also requires you to isolate your domain model from out-of-process dependencies, but for a different reason.

In unit testing, there are two important metrics you need to keep an eye on:

  • The test’s value — How likely the test is to catch a bug.

  • The test’s maintenance cost — How difficult it is to maintain this test moving forward.

Ideally, a test should provide maximum value with minimum maintenance costs.

Your domain model benefits from unit testing the most because the corresponding tests have the greatest chance of catching a critical bug (critical bugs usually reside in business logic).

On the other hand, what makes the code expensive to maintain is the number of collaborators. Code with a large number of collaborators is hard to test — you need additional lines of code to bring those collaborators to an expected condition and then check their state or interactions with them afterward.

The type of the collaborators matters too. Out-of-process collaborators (such as the database) are a no-go when it comes to the domain model. They increase maintenance costs due to the necessity to maintain complicated mock machinery in tests.

And so, to keep maintenance costs manageable while preserving the test’s high value, you must delegate all communications with out-of-process dependencies to classes outside the domain layer. The domain classes then will only work with in-process dependencies, that are easy to deal with without mocking.

Remember, it’s easier to test abstractions than the things they abstract.

Abstract away the application of side effects to external systems. You achieve such abstraction by keeping those side effects in memory until the very end of the business operation, so that they can be tested with plain unit tests without involving out-of-process dependencies.

This mistake is so prevalent in codebases that follow DDD principles that I call it Unit Testing Mistake #1:

"Not isolating the domain model."

To give you a concrete example, here’s the result of a refactoring toward such an isolation I gave in one of my online courses:

[Enable Images]

Notice how arrows (they represent the flow of dependencies) point to the domain classes; none of the errors point from the domain layer to other layers. This means that classes outside the domain model depend on domain classes, but the opposite is not true.

This is an example of an isolated domain model, where domain classes depend on each other, but not on the outside world. Unit testing such a domain model is simple. The resulting tests are highly valuable and easy to maintain.

For more information on how to isolate your domain model from out-of-process dependencies, check out my book:

1. Book discount. Now is a great time to order your copy because Manning (the book’s publisher) recently offered a 40% discount on the book. (I don’t have control over this and they can retract the discount at any time.)

Use the nwsentr40 code during checkout to get the discount. Use this link to purchase the book.

2. Exclusive Bonus. Also, if you buy the book now, you can receive an exclusive bonus: a free enrollment in my course 5 Non-Obvious Tips for Writing Better Unit Tests. Forward your purchase receipt to book@enterprisecraftsmanship.com to redeem this bonus.

Vladimir Khorikov