I'm refactoring a microservice according to Clean Architecture:
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?
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.
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.
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.
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.
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.
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With