Reputation: 822
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?
Upvotes: 14
Views: 6172
Reputation: 5738
Answer:
Answer:
An example of domain layer need to query data from a repository.
// UserAccountApplicationService is an application service belonging to application layer.
class UserAccountApplicationService {
...
public PublicAccountDescriptor registerNewUser(email, password) {
try {
Account newUserData = this.accountFactory.buildUserAccount(email, password);
Account createdAccount = this.accountRepository.save(newUserData);
return new PublicAccountDescriptor(createdAccount);
} catch (e) {
// Exception handling
}
}
}
// AccountFactory belongs to domain layer and acts as a builder.
// AccountFactory need to call AccountRepository method to query data.
class AccountFactory {
...
public Account buildUserAccount(email, password) {
// Password validation, password encryption, etc.
...
Account existingAccount = this.accountRepository.accountOfEmail(email);
if (existingAccount != null) {
throw new Exception("email registered")
}
// Create account instance.
// Publish "account_creation" domain event.
...
return account;
}
}
Upvotes: 8