Skip to main content
6 of 9
added 343 characters in body
Laiv
  • 15k
  • 2
  • 34
  • 72

Front end <--> API Service -> Service -> Repository -> DB

Right. This's the basic design by seggregation of concerns proposed by Spring Framework. So you are in the "Spring's right way".

Despite Repositories are frequently used as DAOs, the truth is that Spring developers took the notion of Repository from Eric Evans' DDD. Repository interfaces will looks often very similar to DAOs because of the CRUD methods and because many developers strive to make repositories's interfaces so generics that, in the end, they have no difference with the EntityManager (the true DAO here).

Repositories are an abstraction addressed to keep the domain unawere of the persistence implementation and technical details. Their solely prupose is serving the domain as it need it. Unlike many developers think, repository interfaces can be totally different from each other.

In Spring Data JPA, the role DAO is played by the EntityManager. It manages the sessions, the access to the DataSource, mappings, etc.

Translated into Spring components, your design is similar to

@RestController > @Service > @Repository >  EntityManager

The Repository is already an abstraction in between services and datastores. When we inherit from Spring Data JPA repository interfaces, we are implementing this.

If extending Spring Data repository interfaces is too much coupling for you or you find that you don't need most of the methods inherited, you can make your own hierarchy of classes.

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private EntityManager em;
    
        public MyRepository (@Autowire EntityManager em){
    
               this.em = em;
         }

        //Interface implentation
        //...
    }

Changing the datasource now just takes a new implementation which replace the EntityManager with a different datasource.

    //@RestController > @Service > @Repository >  RestTemplate

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private RestTemplate rt;
    
        public MyRepository (@Autowire RestTemplate rt){
    
               this.rt = rt;
         }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  File

    @Repository
    public class MyRepositoryImpl implements MyRepository{
       
        private File file; 
        public MyRepository (File file){
    
               this.file = file;
         }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  SoapWSClient

    @Repository
    public class MyRepositoryImpl implements MyRepository{
       
        private MyWebServiceClient wsClient; 
        public MyRepository (@Autowire MyWebServiceClient  wsClient){
    
               this.wsClient = wsClient;
         }

        //Interface implentation
        //...
    }

and so on.

Back to the question, whether you should add one more abstraction layer I would say no. It's not necessary. Your example, IMO, is only adding more complexity. The layer you propose is going to end up as a proxy between services and repositories or as a pseudo-service-repository layer when specific logic is needed.

Finally, if you find Spring's repository interfaces not to be enough, if you need enhance Spring Data interfaces with new methods, Spring allows you to do so. Search a little bit about the BaseRepositoryFactoryBean implentation and @NoRepositoryBean annotation.

Laiv
  • 15k
  • 2
  • 34
  • 72