Repository Pattern - How to understand it and how does it work with "complex" entities?

I'm having a hard time understanding the Repository Pattern.

There are a lot of opinions on that topic like in Repository pattern done right but also other stuff like Repository is the new Singleton or again like in Don't use DAO use Repository or just take Spring JPA Data + Hibernate + MySQL + MAVEN where somehow a Repository appears to be the same as a DAO object.

I'm getting tired of reading this stuff since imho this can't be such a hard thing as it is displayed in a lot of articles.

I see it like this: It appears that what I want is something like this:

         ------------------------------------------------------------------------
         |                            Server                                    |
         ------------------------------------------------------------------------
         |                    |                        |                        |
Client <-|-> Service Layer  <-|->  Repository Layer  <-|-> ORM / Database Layer |
         |                    |                        |                        |  
         ------------------------------------------------------------------------

The Service Layer takes *DTOobjects and passes those to the Repository Layer that basically is nothing more than "the guy" who knows how an entity can be stored.

For example assume you have a composition of some tools (please note that this is just pseudo code)

@Entity
class ToolSet {
  @Id
  public Long id;
  @OneToOne
  public Tool tool1;
  @OneToOne
  public Tool tool2;
}

@Entity
class Tool {
  @Id
  public Long id;
  @OneToMany
  public ToolDescription toolDescription;
}

@Entity
class ToolDescription {
  @Id
  public Long id;
  @NotNull
  @OneToOne
  public Language language

  public String name;
  public String details;
}

The thing I'm not getting is the part where I am getting a ToolSetDTO object from the client.

As I understood it so far I could write a ToolSetRepository with a method ToolSetRepository.save(ToolSetDTO toolSetDto) that "knows how to store" a ToolSetDTO. But almost every tutorial does not pass the *DTO but the Entity instead.

What's bothering me here is that if you take my ToolSet example from above I'd have to do the following steps:

  1. Take toolSetDto and check if not null
  2. For each tool*Dto owned by toolSetDto
    a) If has a valid id then convert from DTO to Entity otherwise create a new database entry
    b) toolDescriptionDto and convert/save it to the database or create a new entry
  3. After checking those above instanciate ToolSet (entity) and set it up for persisting it in the database

All this is too complex to simply let the service function (interface for the client) handle this.

What I was thinking about was creating e.g. a ToolSetRepository but the question here is

  • Does it take a ToolSet entity object or does it use a DTO object?
  • In any case: Is the *Repository allowed to use other repository objects? Like when I want to save ToolSet but I have to store Tool and ToolDescription first - would I use ToolRepository and ToolDescriptionRepository inside ToolSetRepository?
    If so: Why doesn't it break the Repository Pattern? If this pattern is basically a layer between the service and my ORM framework it just does not "feel right" to add dependencies to other *Repository classes due to dependency reasons.

I don't know why I can't get my head around this. It does not sound that complicated but there's still help out there like Spring Data. Another thing that is bothering me since I really don't see how this makes anything easier. Especially since I'm using Hibernate already - I don't see the benefit (but maybe that's another question).

So .. I know this is a long question but I put already a few days of research into it. There's already existing code I am working on right now that starts to become a mess because I just can't see through this pattern.

I hope somebody can give me a bigger picture than most of the articles and tutorials which do not get beyond implementing a very, very simple example of a Repository Pattern.


Solution 1:

You can read my "repository for dummies" post to understand the simple principle of the repository. I think your problem is that you're working with DTOs and in that scenario, you don't really use the repository pattern, you're using a DAO.

The main difference between a repository and a dao is that a repository returns only objects that are understood by the calling layer. Most of the time the repository is used by the business layer and thus, it returns business objects. A dao returns data which might or might not be a whole business object i.e the data isn't a valid business concept.

If your business objects are just data structures, it might be a hint you have a modeling problem i.e bad design. A repository makes more sense with 'rich' or at least properly encapsulated objects. If you're just loading/saving data structures probably you don't need a repository the orm is enough.

If you're dealing with business objects that are composed from other objects (an aggregate) and that object needs all its parts in order to be consistent (an aggregate root) then the repository pattern is the best solution because it will abstract all persistence details. Your app will just ask for a 'Product' and the repository will return it as a whole, regardless of how many tables or queries are required to restore the object.

Based on your code sample, you don't have 'real' business objects. You have data structures used by Hibernate. A business object is designed based on business concepts and use cases. The repository makes it possible for the BL not to care about how that object is persisted. In a way, a repository acts as a 'converter/mapper' between the object and the model that will be persisted. Basically the repo 'reduces' the objects to the required for persistence data.

A business object is not a ORM entity.It might be from a technical point of view, but from a design pov , one models business stuff the other models persistence stuff. In many cases these are not directly compatible.

The biggest mistake is to design your business object according to storage needs and mindset. And contrary to what many devs believe, an ORM purpose is not to persist business objects. Its purpose is to simulate a 'oop' database on top of a rdbms. The ORM mapping is between your db objects and tables, not between app objects (even less when dealing with business objects) and tables.