Domain-Driven Design – Which Layer Do DDD Repositories Belong To?

domain-driven-designlayersrepository-pattern

In his DDD book Evans promotes the idea of layered architecture, and in particular that the business logic should be confined to domain layer and separated from UI/persistence/other concerns. He also introduces Repository pattern as a mean to abstract access and persistent storage to Entities and Value Objects. To me the following left unclear:

  1. Which layer Repositories belong: the Domain Layer, Persistence Layer or something in the middle? (It seems that if it were below Domain Layer it would violate Layered Architecture principle, because it depends on a domain object which it stores)
  2. Can Entities, Value Objects or Domain Services call Repositories?
  3. Should Repositories be abstracted from storage technology (which would be implied if they belong to domain layer) or can they leverage those storage technologies?
  4. Can Repositories contain business logic?
  5. Have you applied these constraints in practice and what was the effect on the quality of your project?

(I am mostly interested in DDD perspective)

Best Answer

Repositories and their placement in the code structure is a matter of intense debate in DDD circles. It is also a matter of preference, and often a decision taken based on the specific abilities of your framework and ORM.

The issue is also muddied when you consider other design philosophies like Clean Architecture, which advocate using an abstract repository in the domain layer while providing concrete implementations in the infrastructure layer.

But here's what I have discovered, and what has worked for me, after trying out different permutation/combinations.

  • From a DDD perspective, Repositories sit between Application Services and Domain Objects.
  • Domain Objects encapsulate behavior and contain the bulk of business logic, enforcing invariants at the aggregate level.
  • Application services receive calls from UI/API/Controllers/Channels (external facing), initial repositories to load aggregates (if needed), invoke domain model for the necessary changes, then use the repositories again to persist aggregates

To your questions:

Which layer Repositories belong: the Domain Layer, Persistence Layer or something in the middle?

  1. I would say there are three distinct layers in DDD applications - the inner domain layer, the outer application layer, and the external world (includes the API/UI).

    The domain layer contains aggregates, entities, value objects, domain services, and domain events. These elements are only dependent on each other and actively avoid dependencies any outer layers.

    The application layer contains Application Services, Repositories, Message Brokers, and whatever else you need to make your application practically possible. This layer is where most of the persistence, authorization, pub-sub processing, etc. happens. Application Layer depends and knows about the domain layer elements, but follows DDD guidelines when accessing the domain model. For example, it will only invoke methods on Aggregates and Domain Services.

    The outermost layer includes API Controllers, serializers, authentication, logging, etc., whatever is not related to business logic or your domain, but very much part of your application.

Can Entities, Value Objects or Domain Services call Repositories?

  1. No. The domain layer should preferably remain agnostic to repositories. Application services should take on the responsibility of transactions and repository interactions.

Should Repositories be abstracted from storage technology (which would be implied if they belong to domain layer) or can they leverage those storage technologies?

  1. Repositories lean towards the domain side, meaning they contain methods that are meaningful from a domain point of view (like GetAdults() or GetMinors()). But the concrete implementation can be done in a couple of ways:

    • You could use an abstract repository to declare the necessary methods, and then create concrete implementations for different databases. The database implementation can be chosen at the beginning of application startup, based on your configurations. Note that even in this case, Domain layer has nothing to do with repositories
    • Repositories could act like wrappers and make use of underlying DAO objects (one per table/document) that implement the actual logic for interacting with the database. The DAO objects are initialized usually with dependency injection if your framework/language supports it, or they could be initialized manually based on active configuration.

Can Repositories contain business logic?

  1. Repositories represent domain concepts, with meaningful method names, but seldom contain any business logic. They encapsulate the database query and give it a conceptual name that usually is derived directly from the ubiquitous language. It is so much better to have a method called GetAdults() instead of .filter(age > 21).

Have you applied these constraints in practice and what was the effect on the quality of your project?

  1. If you restrict yourself to using repositories only in application services, and control transactions at one place (usually with Unit of Work pattern), Repositories are pretty easy to work with. In my past projects, I have found it extremely useful to restrict all database interaction to repositories instead of sprinkling lifecycle methods in the domain layer.

    When I called lifecycle methods (like save, update, etc.) from the aggregate layer, I found it to be extremely complex and difficult to reliably control ACID transactions.