Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA Clean Architecture

I'm refactoring a microservice according to Clean Architecture:

enter image description here

Frameworks should be at the utmost layer. So I used the Adapter Pattern and Dependency Inversion to put org.springframework.data.repository.CrudRepository at the utmost layer. But how can I use @Entity (from Java Persistence API) to persist my entities, if entities are in the center and frameworks are at the utmost layer?


Example: Demo-Entity:

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class Demo implements Serializable {
    @Id
    @GeneratedValue
    private long id;
    
    @NotNull
    private String foo;

}

GenericRepostioryInterface (in the Usecase Layer)

public interface CrudRepositoryInterface<S,T> {
    public <U extends S> U save(U u) ;    
    public <U extends S> Iterable<U> saveAll(Iterable<U> itrbl) ;    
    public Optional<S> findById(T id) ;    
    public boolean existsById(T id) ;    
    public Iterable<S> findAll() ;    
    public Iterable<S> findAllById(Iterable<T> itrbl) ;    
    public long count() ;    
    public void deleteById(T id) ;    
    public void delete(S t);    
    public void deleteAll(Iterable<? extends S> itrbl) ;    
    public void deleteAll() ;  
}

Some usecase:

    @Autowired
    private CrudRepositoryInterface<Demo,Long> demoRepository;
    ...
    
    private void deleteAll(){
      this.demoRepository.deleteAll();
    }
    ...

Adapter (DB Layer)

public interface DemoRepositoryAdapter extends CrudRepository<Demo,Long>,CrudRepositoryInterface<Demo,Long>{    
}

Config for Injection (I put that in the DB Package/Layer as well)

@Configuration
public class InjectRepositoryConfig {    
    @Bean
    public CrudRepositoryInterface<Demo,Long> animalOwnerRepository(@Autowired DemoRepositoryAdapter demoRepositoryAdapter){
        return demoRepositoryAdapter;
    }
}

This works fine so far but I'm unsure how to remove / replace / refactor JPA out of the core layer?

like image 491
Barney Stinson Avatar asked Sep 30 '18 09:09

Barney Stinson


People also ask

What is meant by clean architecture?

Clean architecture is a software design philosophy that separates the elements of a design into ring levels. An important goal of clean architecture is to provide developers with a way to organize code in such a way that it encapsulates the business logic but keeps it separate from the delivery mechanism.

What is clean architecture in Java?

Clean architecture is a staple of the modern app development space. Particularly popular for Java and Android developers, this architecture is designed to make it easier to create stable apps even when outer elements such as UI, databases, or external APIs are always changing.

What is clean architecture in spring boot?

The clean architecture compiles many code designs and principles, like SOLID, stable abstractions, and others. But, the core idea is to divide the system into levels based on the business value.

What are the four layers of clean architecture?

In contrast to general architecture, Clean Architecture does not depend on frameworks, user interfaces, or databases. Generally, an application has 3 kinds of layers: UI layer, Business Logic Layer, and Data Access Layer but Clean architecture includes Application Core, Infrastructure and UI.


2 Answers

I think the general confusion here is due to the overloading of the term Entity, which has different semantics across different contexts. In the context of JPA, an Entity is a persistence abstraction representing rows in a table and ORM. In the context of Clean Architecture, an Entity is a business domain abstraction and is completely independent of persistence.

They can co-exist in a Clean Architecture, but they each serve distinct purposes. Attempting to combine them and leverage JPA capabilities in your business domain entities violates the principles of Clean Architecture and will couple your domain to your persistence implementation.

like image 133
Cory Serratore Avatar answered Oct 16 '22 12:10

Cory Serratore


If you really want to follow clean architecture, then no reference to external libraries should be in your domain classes. To overcome that, a couple of strategies could be:

  • User jpa/hibernate XML mappings. This way you can externalize your mappings and change between sql/noSql easily just importing a different config file (each file will setup a different set of XML mappings).

The downside of this approach, is that your domain will still require to implement Serializable, and most of them will require setters/getters for its fields, or force hibernate/jpa to ignore Java limitations and access private fields. Also, there are things you can do with Annotations that is not possible with XML, or the workaround is problematic. It works, but still your domain layer looks like simple DTOs.

  • When crossing a boundary, use DTO objects. This means you will have your domain object Demo, and a DTO object DemoData for example. Data will be retrieved using DemoData instances, and repository should convert between those two when saving/retrieving.

This last approach makes you project very easy to change. You can change persistence layer any time, but doing so will require a whole new datastore layer to integrate. Let's say you want to change to cassandra, you will need a new DTO DemoDataCassandra, its mappers between it to Demo, new annotations for mappings, etc.

like image 40
Jhovanni Avatar answered Oct 16 '22 11:10

Jhovanni