DAO vs modelli di repository

1. Panoramica

Spesso, le implementazioni di repository e DAO sono considerate intercambiabili, specialmente nelle app incentrate sui dati. Questo crea confusione sulle loro differenze.

In questo articolo, discuteremo le differenze tra DAO e modelli di repository.

2. Pattern DAO

Il Data Access Object Pattern, noto anche come DAO Pattern , è un'astrazione della persistenza dei dati ed è considerato più vicino allo storage sottostante, che è spesso incentrato sulla tabella .

Pertanto, in molti casi, i nostri DAO corrispondono alle tabelle del database, consentendo un modo più diretto per inviare / recuperare i dati dalla memoria, nascondendo le brutte query.

Esaminiamo una semplice implementazione del pattern DAO.

2.1. Utente

Innanzitutto, creiamo una classe di dominio utente di base :

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

2.2. UserDao

Quindi, creeremo l' interfaccia UserDao che fornisce semplici operazioni CRUD per il dominio utente :

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

2.3. UserDaoImpl

Infine, creeremo la classe UserDaoImpl che implementa l' interfaccia 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); } // ... }

Qui, per semplicità, abbiamo utilizzato l' interfaccia JPA EntityManager per interagire con lo storage sottostante e fornire un meccanismo di accesso ai dati per il dominio utente .

3. Modello di repository

Secondo il libro di Eric Evans Domain-Driven Design , "il repository è un meccanismo per incapsulare il comportamento di archiviazione, recupero e ricerca, che emula una raccolta di oggetti".

Allo stesso modo, secondo Patterns of Enterprise Application Architecture , "media tra il dominio e i livelli di mappatura dei dati utilizzando un'interfaccia simile a una raccolta per accedere agli oggetti del dominio".

In altre parole, un repository si occupa anche di dati e nasconde query simili a DAO. Tuttavia, si trova a un livello superiore, più vicino alla logica di business di un'app.

Di conseguenza, un repository può utilizzare un DAO per recuperare i dati dal database e popolare un oggetto di dominio. Oppure può preparare i dati da un oggetto dominio e inviarli a un sistema di archiviazione utilizzando un DAO per la persistenza.

Esaminiamo una semplice implementazione del pattern Repository per il dominio utente .

3.1. UserRepository

Innanzitutto, creiamo l' interfaccia UserRepository :

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

Qui, abbiamo aggiunto alcuni metodi comuni come get , add , update e remove per lavorare con la raccolta di oggetti.

3.2. UserRepositoryImpl

Quindi, creeremo la classe UserRepositoryImpl fornendo un'implementazione dell'interfaccia 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); } // ... }

Qui, abbiamo utilizzato UserDaoImpl per inviare / recuperare dati dal database.

Finora, possiamo dire che le implementazioni di DAO e del repository sembrano molto simili perché la classe User è un dominio anemico. E un repository è solo un altro livello sul livello di accesso ai dati (DAO).

Tuttavia, DAO sembra un candidato perfetto per accedere ai dati e un repository è un modo ideale per implementare un caso d'uso aziendale .

4. Modello di repository con più DAO

Per comprendere chiaramente l'ultima affermazione, miglioriamo il nostro dominio utente per gestire un caso d'uso aziendale.

Immagina di voler preparare un profilo sui social media di un utente aggregando i suoi tweet su Twitter, i post di Facebook e altro ancora.

4.1. Tweet

Innanzitutto, creeremo la classe Tweet con alcune proprietà che contengono le informazioni del tweet:

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

4.2. TweetDao and TweetDaoImpl

Then, similar to the UserDao, we'll create the TweetDao interface that allows fetching tweets:

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

Likewise, we'll create the TweetDaoImpl class that provides the implementation of the fetchTweets method:

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

Here, we'll call Twitter APIs to fetch all tweets by a user using his email.

So, in this case, a DAO provides a data access mechanism using third-party APIs.

4.3. Enhance User Domain

Last, let's create the UserSocialMedia subclass of our User class to keep a list of the Tweet objects:

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.