8

I'm working in an architecture, it is going to offer a rest api for web client and mobile apps. I'm using Spring(spring mvc, spring data jpa, ...etc). The domain model is coded with JPA specification.

I'm trying to apply some concepts of clean architecture (https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html). Not all, because I'm going to keep the jpa domain model.

The actual flow through the layers is this:

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

  • Front end: web client, mobile apps
  • API Service: Rest Controllers, here I use converters and dto's and call services
  • Service: Interfaces with implementations and they contain business logic
  • Repository: Repository interfaces with automatically implementations(done by spring data jpa) which contatin CRUD operations and maybe some sql queries

My doubt: Should I use an extra layer between service and repository?

I'm planning this new flow:

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

Why to use this persistence layer? As it says in the clean architecture article I would like to have a service implementation(business logic or use case) that access an agnostic persistence layer. And not changes will be needed if I decide to use another "data access" pattern, for example if I decide to stop using repository.

class ProductServiceImpl implements ProductService {
    ProductRepository productRepository;
    void save(Product product) {
        // do business logic
        productRepository.save(product)
    }
}

So I'm thinking use a persistence layer like this:

class ProductServiceImpl implements ProductService {
    ProductPersistence productPersistence;
    void save(Product product) {
        // do business logic
        productPersistence.save(product)
    }
}

and implementation of the persistence layer like this:

class ProductPersistenceImpl implements ProductPersistence {
    ProductRepository productRepository;
    void save(Product product) {
        productRepository.save(product)
    }
}

So I only need to change the implementations of persistence layer, left the service without changes.Coupled with the fact that the Repository is related with the framework.

What do you think? Thanks.

  • 1
    the repository IS the abstraction layer. adding another one doesnt help – Ewan May 10 '17 at 9:35
  • Oh, but I would have to use the interface proposed by Spring, I mean, repository method names. And if I want to change the repository I would have to keep the calling names, No? – Alejandro May 10 '17 at 17:20
  • it looks to me like the spring repository doesnt force you to expose spring objects. Just use it to implement an agnostic interface – Ewan May 10 '17 at 18:02
4

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

Front end: web client, mobile apps

API Service: Rest Controllers, here I use converters and dto's and call services

Service: Interfaces with implementations and they contain business logic

Repository: Repository interfaces with automatically implementations(done by spring data jpa) which contatin CRUD operations and maybe some sql queries

This's the design proposed by Spring. So you are in the "Spring's right way".

Repositories are commonly called DAOs but from the design point of view, Repository and DAO are different components with different responsabilities.

Repository pattern belongs to a higher level of abstraction. It's a domain object. Closer to the business than the DAOs.

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

Translated to Spring components, your design looks similar to:

@RestController > @Service > @Repository > EntityManager

As you see, the Repository is already an abstraction between services and the data store.

If extending Spring's repositories is too much coupling, you can do this

@Repository
public class MyRepositoryImpl implements MyRepository{
    private EntityManager em;

    public MyRepository (@Autowire EntityManager em){

           this.em = em;
     }

    //Interface implentation
    //...
}

Now, we can just replace the data store.

@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 > WebServiceClient

@Repository
public class MyRepositoryImpl implements MyRepository{

    private MyWebServiceClient wsClient; 
    public MyRepository (@Autowire MyWebServiceClient  wsClient){

           this.wsClient = wsClient;
     }

    //Interface implentation
    //...
}

MyRepository interface may or may not extends Spring's Repository interface. Depends on your needs. Take a look to the hierarchy of interfaces.

Back to the question if you should add one more abstraction layer, I would say no, It's no requiered. I would rather take profit of the framework's capabilities.

The example you exposed , 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.

Finally, if you find Spring's repositories interfaces not to be enough, if you need enhance the interfaces and provide new methods , Spring also allows you to do so. You will find examples googling by: BaseRepositoryFactoryBean implentation and @NoRepositoryBean

3

The best way to prove that a design is flexible is to flex it.

You want a place in your code that is responsible for persistence but is not tied to the idea of using a repository. Fine. It's not doing anything useful at the moment... sigh, fine.

OK, lets test if this shunt layer did any good. Create a flat file layer that will persist your products in files. Now where does this new layer go in this design?

Well it should be able to go where DB is. After all we don't need DB anymore since we're using flat files. But this was also supposed to not need repository.

Consider, maybe repository is an implementation detail. After all I can talk to the DB without using the repository pattern.

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

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

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

Front end <--> API Service -> Service -> Persistence -> Files

If you can make all that work without touching Service you have flexible code.

But don't take my word for it. Write it and see what happens. The only code I trust to be flexible is flexed code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.