Real-life DDD: organise code by responsibility layers, not repositories and services

Here is a screenshot from Visual Studio from a project for a client that I have been lead developer on for the past few months. The domain (actually a bounded context within a larger ERP system) is training for an emergency services department, where staff are required to be proficient in all sorts of skills like using rescue equipment and performing first aid.

On the left is what the domain layer looked like when we began several months ago. On the right is how it looks today:

Before: repositories, specifications, services. After: responsibility layers

What do you notice? Obviously the domain model has grown bigger and more complex over time (including another 10-20 classes that didn’t fit in my screenshot). But more importantly our grouping of objects has changed.

Responsibility layers

We originally started grouping classes into namespaces based on their object type — we had separate namespaces for repositories, specifications, factories and services.

This method made sense when we just began and our domain was very small (just a handful of core objects), but did not scale as complexity grew. For example, one important aspect of the model that arose was the separation of practicing from monitoring, i.e.:

  • scheduling training tasks, choosing which skills are to be practiced, choosing who will participate etc versus
  • determining which skills a staff member is required to maintain according to their location and position, looking back and seeing when the last time they practiced skill X, applying policies to see if it’s time to practice it again, and looking at the overall operational status of individuals, teams etc

You can see this separation reflected in the screenshot on the right.

When we first put this split in, and others like it, we preserved this grouping inside each layer — e.g. we had a Practicing.Services and Monitoring.Services namespace. This dual classification felt wrong however, and led to an annoying proliferation of namespaces that required gazillions of using statements that we had to fix every time something shifted or renamed.

After a while, we pulled objects back to half a dozen big namespaces with 10-30 classes each, supplemented by a few smaller specific domain policy and state groups. These new namespaces are beginning to show the responsibility layers of our domain, and give a much clearer picture than simply grouping all the specifications together.