Modèles DAO vs référentiel

1. Vue d'ensemble

Souvent, les implémentations du référentiel et de DAO sont considérées comme interchangeables, en particulier dans les applications centrées sur les données. Cela crée de la confusion sur leurs différences.

Dans cet article, nous discuterons des différences entre les modèles DAO et Repository.

2. Modèle DAO

Le modèle d'objet d'accès aux données, alias modèle DAO , est une abstraction de la persistance des données et est considéré comme plus proche du stockage sous-jacent, qui est souvent centré sur la table .

Par conséquent, dans de nombreux cas, nos DAO correspondent aux tables de base de données, ce qui permet un moyen plus simple d'envoyer / récupérer des données à partir du stockage, masquant les requêtes laides.

Examinons une implémentation simple du modèle DAO.

2.1. Utilisateur

Tout d'abord, créons une classe de domaine utilisateur de base :

public class User { private Long id; private String userName; private String firstName; private String email; // getters and setters }

2.2. UserDao

Ensuite, nous allons créer l' interface UserDao qui fournit des opérations CRUD simples pour le domaine utilisateur :

public interface UserDao { void create(User user); User read(Long id); void update(User user); void delete(String userName); }

2.3. UserDaoImpl

Enfin, nous allons créer la classe UserDaoImpl qui implémente l' interface UserDao :

public class UserDaoImpl implements UserDao { private final EntityManager entityManager; @Override public void create(User user) { entityManager.persist(user); } @Override public User read(long id) { return entityManager.find(User.class, id); } // ... }

Ici, pour plus de simplicité, nous avons utilisé l' interface JPA EntityManager pour interagir avec le stockage sous-jacent et fournir un mécanisme d'accès aux données pour le domaine utilisateur .

3. Modèle de référentiel

Selon le livre d'Eric Evans, Domain-Driven Design , le «référentiel est un mécanisme d'encapsulation du comportement de stockage, de récupération et de recherche, qui émule une collection d'objets».

De même, selon Patterns of Enterprise Application Architecture , il «sert d'intermédiaire entre les couches de mappage de domaine et de données à l'aide d'une interface de type collection pour accéder aux objets du domaine».

En d'autres termes, un référentiel traite également les données et masque les requêtes similaires à DAO. Cependant, il se situe à un niveau supérieur, plus proche de la logique métier d'une application.

Par conséquent, un référentiel peut utiliser un DAO pour extraire des données de la base de données et remplir un objet de domaine. Ou, il peut préparer les données à partir d'un objet de domaine et les envoyer à un système de stockage à l'aide d'un DAO pour la persistance.

Examinons une implémentation simple du modèle de référentiel pour le domaine utilisateur .

3.1. UserRepository

Commençons par créer l' interface UserRepository :

public interface UserRepository { User get(Long id); void add(User user); void update(User user); void remove(User user); }

Ici, nous avons ajouté quelques méthodes courantes comme obtenir , ajouter , mettre à jour et supprimer pour travailler avec la collection d'objets.

3.2. UserRepositoryImpl

Ensuite, nous allons créer la classe UserRepositoryImpl fournissant une implémentation de l' interface UserRepository :

public class UserRepositoryImpl implements UserRepository { private UserDaoImpl userDaoImpl; @Override public User get(Long id) { User user = userDaoImpl.read(id); return user; } @Override public void add(User user) { userDaoImpl.create(user); } // ... }

Ici, nous avons utilisé UserDaoImpl pour envoyer / récupérer des données de la base de données.

Jusqu'à présent, nous pouvons dire que les implémentations de DAO et du référentiel sont très similaires car la classe User est un domaine anémique. Et, un référentiel est juste une autre couche sur la couche d'accès aux données (DAO).

Cependant, DAO semble un candidat parfait pour accéder aux données, et un référentiel est un moyen idéal de mettre en œuvre un cas d'utilisation métier .

4. Modèle de référentiel avec plusieurs DAO

Pour bien comprendre la dernière déclaration, améliorons notre domaine utilisateur pour gérer un cas d'utilisation métier.

Imaginez que nous souhaitons préparer un profil de réseau social d'un utilisateur en agrégeant ses tweets Twitter, ses publications Facebook, etc.

4.1. Tweet

Tout d'abord, nous allons créer la classe Tweet avec quelques propriétés qui contiennent les informations du tweet:

public class Tweet { private String email; private String tweetText; private Date dateCreated; // getters and setters }

4.2. TweetDao et TweetDaoImpl

Ensuite, à l' instar de UserDao , nous allons créer l' interface TweetDao qui permet de récupérer des tweets:

public interface TweetDao { List fetchTweets(String email); }

De même, nous allons créer la classe TweetDaoImpl qui fournit l'implémentation de la méthode fetchTweets :

public class TweetDaoImpl implements TweetDao { @Override public List fetchTweets(String email) { List tweets = new ArrayList(); //call Twitter API and prepare Tweet object return tweets; } }

Ici, nous appellerons les API Twitter pour récupérer tous les tweets d'un utilisateur à l'aide de son e-mail.

Ainsi, dans ce cas, un DAO fournit un mécanisme d'accès aux données à l'aide d'API tierces.

4.3. Améliorer le domaine utilisateur

Enfin, créons la sous-classe UserSocialMedia de notre classe User pour conserver une liste des objets Tweet :

public class UserSocialMedia extends User { private List tweets; // getters and setters }

Here, our UserSocialMedia class is a complex domain containing the properties of the User domain too.

4.4. UserRepositoryImpl

Now, we'll upgrade our UserRepositoryImpl class to provide a User domain object along with a list of tweets:

public class UserRepositoryImpl implements UserRepository { private UserDaoImpl userDaoImpl; private TweetDaoImpl tweetDaoImpl; @Override public User get(Long id) { UserSocialMedia user = (UserSocialMedia) userDaoImpl.read(id); List tweets = tweetDaoImpl.fetchTweets(user.getEmail()); user.setTweets(tweets); return user; } }

Here, the UserRepositoryImpl extracts user data using the UserDaoImpl and user's tweets using the TweetDaoImpl.

Then, it aggregates both sets of information and provides a domain object of the UserSocialMedia class that is handy for our business use-case. Therefore, a repository relies on DAOs for accessing data from various sources.

Similarly, we can enhance our User domain to keep a list of Facebook posts.

5. Comparing the Two Patterns

Now that we've seen the nuances of the DAO and Repository patterns, let's summarize their differences:

  • DAO is an abstraction of data persistence. However, a repository is an abstraction of a collection of objects
  • DAO is a lower-level concept, closer to the storage systems. However, Repository is a higher-level concept, closer to the Domain objects
  • DAO works as a data mapping/access layer, hiding ugly queries. However, a repository is a layer between domains and data access layers, hiding the complexity of collating data and preparing a domain object
  • DAO can't be implemented using a repository. However, a repository can use a DAO for accessing underlying storage

Also, if we have an anemic domain, the repository will be just a DAO.

Additionally, the repository pattern encourages a domain-driven design, providing an easy understanding of the data structure for non-technical team members, too.

6. Conclusion

In this article, we explored differences between DAO and Repository patterns.

First, we examined a basic implementation of the DAO pattern. Then, we saw a similar implementation using the Repository pattern.

Last, we looked at a Repository utilizing multiple DAOs, enhancing the capabilities of a domain to solve a business use-case.

Therefore, we can conclude that the Repository pattern proves a better approach when an app moves from being data-centric to business-oriented.

As usual, all the code implementations are available over on GitHub.