'DDD - Repository interfaces in domain or application layer?
There are several posts on stackoverflow about this question, but I'm still not able to understand it fully.
Different architectures like the layered architecture in the blue book, Ports and Adapters, Onion Architecture or Clean Architecture are proposed. Despite after much reading I still don't fully understand why other approaches are proposed, but each one isolates the domain as postulated by Eric Evans.
In my Web API project I use an Web API layer that holds the controllers, an application layer that handles and orchestrates the business use cases, the domain layer, and a persistence layer that implements the repositories using EF Core to access the database. The API layer calls commands of the application layer that handlers process. Later other API layers may be added for GraphQL or gRPC.
Eric Evans Domain-Driven Design:
"The infrastructure layer usually does not initiate action in the domain layer. Being “below” the domain layer, it should have no specific knowledge of the domain it is serving."
I understand that the infrastructure layer usually does not initiate action in the domain layer, but I don't understand how it should have no specific knowledge of the domain. How can the repository save an entity if it doesn't have knowledge of the entity?
Eric Evans Domain-Driven Design:
The application and domain layers call on the SERVICES provided by the infrastructure layer.
In other posts on stackoverflow and articles it is stated that the domain layer should not depend on the repository or other services and that rather the application service will access those and provide the results to the domain layer. For example an application command handler will get an aggregate by id from the repository and then calls the domain commands on that aggregate and then updates the entity in the repository.
If I understand Clean Architecture and Onion Architecture correctly, the domain layer is at the center and does not access any outer layers. Does this contradict Evans or is it just an improvement?
I often miss information about the relationships in the infrastructure layer. Looking at the different architectures I would consider my Web API layer part of the infrastructure layer. If the Web API controller and the repository implementation are in the same layer, the API could call the repository directly and bypass the application service. This makes no sense to me. I rather have the API depend on the application layer only.
This matches also the image of the onion architecture:
In that case it makes no sense to me to have the repository interface in the domain layer. I would have it in the application layer. According to Uncle Bob the interface is owned by the client. Therefore I would have the repository interfaces in the application layer.
Commonly it is stated that the repository interface is part of the domain layer. Is this only related to the different architecture styles? If I understand correctly, in Evans approach the domain layer can access the infrastructure and in clean or onion architecture it does not. Is that correct?
Can someone help me to clear this mess in my head and especially explain why which approach is used? In what cases would it make sense that domain layers calls infrastructure and in what cases not?
Solution 1:[1]
Why do we need to introduce Repository Interface in the domain layer?
Answer:
- For abstraction and loose coupling purpose. Thanks to the interface declaration in the domain layer, detailed implementation in infrastructure layer can be flexibly modified without worrying about changing domain logic.
- For dependency integrity (outer-depends-on-inner), domain entities and domain services sometimes DO need to query data for some specific operations. By introducing Repository Interface, the domain entities and domain services just need to depend on the interfaces and access the implementation instances by using Dependency Injection or Service Factory.
Why not let GraphQL Resolvers, Restful Controllers, RPC Handlers, etc (request handlers in general) directly use the infra-Repositories-Implementation-Classes for better simplicity, as they're both in the most-outer layers? Why make the process flow to be too complex?
Answer:
- Because they just accept the clients request and don't know anything about the use cases (belong to application service) and domain business logic (belong to domain layer). This is especially true for "write" operation (e.g: bank transfer).
- Even for "read" operations, there are some cases we need business logics involved like authorization check, remove some sensitive data fields (e.g: password from account) before returning to clients. These cannot be done in the outer layer.
A couple of things to be clear
- "Access / Call" is different to "Depend / Import". Domain services and entities "access / call" repository instances to find the aggregates, doesn't mean they "depend on / import" repository implementation.
- Domain services and entities sometimes DO NEED to query data to complete their operations.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | haotang |