How to implement DDD using Spring Crud/Jpa Repository
Asked Answered
L

1

5

I want to create an app by implementing DDD using Spring. Let's say I have a business entity Customer and an interface CustomerRepository.

Since spring provides CrudRepository and JpaRepository to perform basic CRUD operations and other operations like finder methods by default I want to use them. So my interface becomes

 @Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>{

}

But according to DDD, interfaces should be in domain layer and the implementation should be in Infrastructure layer.

Now my question is, which layer the CustomerRepository belongs to?

Luggage answered 20/4, 2018 at 5:57 Comment(0)
R
9

Short answer: although it should be any dependencies to Infrastructure in the Domain layer, for the sake of KISS, you may to do so. If you want to be a DDD purist, you define a CustomerRepository interface AND an implementation in the Infrastructure that implements both the interfaces.

Long and boring answer: In general, the Domain should not care or know about infrastructure, as in, it should not have dependencies to other layers (Infrastructure, Application, Presentation or whatever architecture you are using). Following this rule leads to a cleaner architecture.

In particular, the domain should not care about the persistence, it should behave as it runs in memory. From the Domain point's of view, the Entities mutate and that's all, no persistence is needed.

The Write side of the Domain code doesn't in fact need persistence. When the Aggregates execute commands, they are already fully loaded. And after the command is executed, the Aggregates just return the changes or the new state. The Aggregates don't persist the changes themselves. They are pure, with no observable side effects.

We, the architects, need persistence because we need to ensure that the data persist between restarts and that we can run the same code on multiple machines on the same time.

There is however, another need that the Domain code, in particular the Read and the Reactive side of the Domain (Sagas/Process managers). These components of the Domain need to query and filter the Domain Entities. The Readmodels need to return the Entities to the caller and the Sagas/Process managers need to correctly identify the right Aggregates to whom to send the Commands.

The solution is to define only the Interfaces in the Domain layer and to have implementations in the Infrastructure. In this way the Domain owns the Interface so, according to the Dependency Inversion Principle, it does not depend on the Infrastructure.

In your case, although the Domain layer depends on something from the Infrastructure part of the Spring Framework, that something is only an Interface. It is still a depency to JPA because your domain will use methods that it doesn't own but KISS could more important in this case.

The alternative would be do define an Interface that does not extend the JpaRepository with an implementation in the Infrastructure that implements this Interface and the JpaRepository Interface.

Which solution depends on you: more code duplication but less dependency or less code duplication and more dependency to JPA.

Riggall answered 20/4, 2018 at 7:18 Comment(7)
Thanks for the detailed explanation Constantin Galbenu. From what i understood is u gave 2 solutions : 1. To define an interface(IRepository) as marker interface in domain layer and implement it in infrastructure layer along with JpaRepository(public interface CustomerRepository extends IRepository,JpaRepository<Customer, Long>). and 2. Second solution is to define public interface CustomerRepository extends JpaRepository<Customer, Long> directly in infrastructure layer and use it in domain(Since it is only an interface). I didnot understand that KISS partLuggage
@Luggage ` 2. Second solution is to define public interface CustomerRepository extends JpaRepository<Customer, Long> directly in infrastructure layer and use it in domain`: Almost, you define it in the Domain layerRiggall
Just for additional info, I've had the same problem. Ive done this: interface in domain layer, exactly as you need. A class implements the interface in the infrastructure layer. The implementation, to be "operative", uses the JPA interface from Spring. In this way the domain repository is exactly as you want it, and it could be "stubbed" everywhere you need for tests. On the other side you still use JPA so you don't loose all the advantages of it.Unbolt
@LucaMasera So u mean ICustomerRepository in domain layer and (CustomerRepository extends ICustomerRepository,JpaRepository<Customer,Long> ) class in infrastructure layer right this is similar to what Constantin Galbenu suggested which may lead to code duplication and there is no way to force the developer to implement the ICustomerRepository because we are extending ICustomerRepository or is there anything im missingLuggage
I've done this: 1. Interface ICustomerRepository in the Domain, with some functions which names are more connected to the domain language 2. A class CustomerRepositoryAdapter, that implements the interface, in the Infrastructure layer 3. The JPA interface, ICustomerRepositoryJPA, that uses JPA to work with the database. The class CustomerRepositoryAdapter uses the JPA interface to work. This because I've used composition over inheritance to get the work done. In this way, if needed, you could have a Domain Entity that's different from the JPA Entity used by the database.Unbolt
hello, why there anyone can provide please an example of the implementation it will be very easy to understand instead of writing large paragrpah. we understand code more than words. regardsTingey
@ConstantinGalbenu the code that is using that repository depends not only on the spring repository interface but also on the Customer, that is the DAO and not the entity domain. So using the Spring interface breaks two times the domain dependency rule. A more DDD purist solution is to define the repository interface in the domain and the implementation in infrastructure that converts the domain model to the Spring DAO, and then USE the spring repository to finally store the domain entityOvermatch

© 2022 - 2024 — McMap. All rights reserved.