0

I'm using Spring Boot with JPA / Hibernate and I'm trying to decouple the business layer from the data layer. I would like to be able to (relatively easily) switch from a relational database to a rdf triple store.

Here is a partial simplified description of my current try. I have entities such as:

@Entity(name="Drawer")
public class DrawerEntity {
    @Id
    private int id;
    private String label;

    public int getId(){return id;}
    public void setId(int id){this.id=id;}

    public String getLabel(){return label;}
    public void setLabel(String label){this.label=label;}

}

I have business objects wich are acting as proxy to entities such as:

public class Drawer{
    @Autowired
    private CardRepository cardRepository;
    private DrawerEntity drawerEntity;

    public Drawer(){this.drawerEntity = new DrawerEntity();}
    public Drawer(DrawerEntity drawerEntity){this.drawerEntity = drawerEntity;}

    public int getId(){return drawerEntity.getId();}
    public void setId(int id){drawerEntity.setId(id);}

    public String getLabel(){return drawerEntity.getLabel();}
    public void setLabel(String label){drawerEntity.setLabel(label);}

    public int getNumberOfCards(){return cardRepository.countByDrawerId(this.getId());}
}

I've also a service layer :

public interface DrawerService {
    Collection<Drawer> findAll();
    Drawer findOne(int id);
    Drawer create(Drawer drawer);
    void delete(int id);   
}

Implemented by a DrawerServiceBean

@Service
public class DrawerServiceBean implements DrawerService {

    @Autowired
    private DrawerRepository drawerRepository;

    @Override
    public Collection<Drawer> findAll() {
        Collection<Drawer> allCard = new ArrayList<>();
        for(DrawerEntity drawerEntity : drawerRepository.findAll()){
           allCard.add(new Drawer(drawerEntity)); 
        }
        return allCard;
    }

    @Override
    public Drawer findOne(int id) {return new Drawer(drawerRepository.findOne(id));}

    @Override
    public Drawer create(Drawer drawer) {return new Drawer(drawerRepository.save(drawer.getDrawerEntity()));}

    @Override
    public void delete(int id) {drawerRepository.delete(id);}
}

Which is linked to a JPA repository

@Repository
public interface DrawerRepository extends JpaRepository<DrawerEntity, Integer> {}

Following the advices given in this answer, I'm not injecting anything into the entities and I'm using business proxy objects. But then, it seems that, I have to use @Configurable and AspectJ LTW for injecting the repositories into the business objects because "Spring AOP only supports method execution join points for Spring beans".

The goal of this architecture was to be able to define methods such as getNumberOfCards in business objects that use different types of repositories (CardRepository, DrawerRepository). But the result seems complex with a lot of boilerplate code. I wonder how I can simplify this? Is Aspect Oriented Programming necessary? Can I reduce the size of the boilerplate code? Is there some solutions provided by Spring Data?

  • To decouple business and data layer, the business layer needs a public interface that will act as the "contract" to be implemented by every Repository. This contract also includes the data model required (normally POJOS). Every candidate to be business's data provider (repository) must implement such interface and the way to translate data into the business interface data model. The answers you linked are not related to the subject of your question (IMO). – Laiv Sep 12 '16 at 14:22
  • The public business interface is, here, DrawerService and the POJO Drawer. The problem is how to encapsulate the JPA DrawerEntity to present an enriched business object such as Drawer. – Ortomala Lokni Sep 12 '16 at 15:24
  • Spring is also a Bean Factory. So far I see, you are trying to keep Spring @Autowire in classes that are going to be instantiated manually. Right? Would not be easier to implement a DrawerFactoryBean whit access to Spring ApplicationContext instance and use your Factory to ask Spring for new Instances of Drawer? Let Spring to do IoC or do it yourself manually but I avoid hybrids. DrawerFactoryBean and Drawer need to be declared as @Component. Finally set a different Spring scope to Drawer with @Scope Set it as Prototype to avoid spring's singleton. – Laiv Sep 12 '16 at 20:10
  • This should answer my question: stackoverflow.com/questions/15117403/… – Ortomala Lokni Sep 26 '16 at 20:09
1

Sometime ago, i asked on SO a question that include if i have to implements my service as they're using JPA managed entities ? Here are the two points of my own answer that should interest you :

  • I don't have to abstract the fact that i use managed entities, this will lead to complex and unefficient code

  • I choosed JPA, i won't switch for it which is true unless rewriting the full model to stand for something based on non relationnal database.

If you try to abstract the fact that you're using lazy-loading/managed entities, you will just have troubles. For instance, i saw a project where there was DAO Pojo and business POJO, they were always converting the DAO to business POJO using adapter, and thus always triggering loading lazy list in the N+1 select way.

So if I adapt what i was told in that question, at the very least, your should have 2 differents business implementations, one relying on the strength of JPA, and the other, either one that don't care about JPA/RDF, either one that will get the best of RDF DAO layer.

I know that doesn't seems to follow the rule of POO about totally encapsulating behaviour in each layer, but really, try to abstract complex JPA model without triggering cascading N+1 select on every relations or manually fetching each relations when you shouldn't need to with JPA. It just can't work. JPA is really invasive, but really powerful too, as you have very few code to write (and maintain!) to use it.

Of course if you're just doing something really simple having quite few data, a demo, ... you can stick to one business layer that is not using JPA at his best.

But if you need some basic performance, like loading all your database line-by-line in the ORM is not an option, you will probably end up stuck with one implementations that don't use JPA right.

  • I was hoping for a better solution but I think you are right. I removed business objects and use directly entities with only getters and setters. I'm using services for serving extended functionalities, – Ortomala Lokni Sep 14 '16 at 12:57

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.