DAO vs Repository Patterns

1. Übersicht

Insbesondere in datenzentrierten Apps werden die Implementierungen von Repository und DAO häufig als austauschbar angesehen. Dies schafft Verwirrung über ihre Unterschiede.

In diesem Artikel werden die Unterschiede zwischen DAO- und Repository-Mustern erläutert.

2. DAO-Muster

Das Datenzugriffsobjektmuster, auch bekannt als DAO-Muster , ist eine Abstraktion der Datenpersistenz und wird als näher am zugrunde liegenden Speicher betrachtet, der häufig tabellenzentriert ist .

Daher stimmen unsere DAOs in vielen Fällen mit Datenbanktabellen überein, was eine einfachere Möglichkeit zum Senden / Abrufen von Daten aus dem Speicher ermöglicht und die hässlichen Abfragen verbirgt.

Lassen Sie uns eine einfache Implementierung des DAO-Musters untersuchen.

2.1. Nutzer

Erstellen wir zunächst eine grundlegende Benutzerdomänenklasse :

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

2.2. UserDao

Anschließend erstellen wir die UserDao- Schnittstelle, die einfache CRUD-Operationen für die Benutzerdomäne bereitstellt :

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

2.3. UserDaoImpl

Zuletzt erstellen wir die UserDaoImpl- Klasse, die die UserDao- Schnittstelle implementiert :

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); } // ... }

Hier haben wir der Einfachheit halber die JPA EntityManager- Schnittstelle verwendet, um mit dem zugrunde liegenden Speicher zu interagieren und einen Datenzugriffsmechanismus für die Benutzerdomäne bereitzustellen .

3. Repository-Muster

Gemäß Eric Evans 'Buch Domain-Driven Design ist das „Repository ein Mechanismus zum Einkapseln von Speicher-, Abruf- und Suchverhalten, der eine Sammlung von Objekten emuliert.“

Ebenso nach Patterns of Enterprise Application Architecture , es „vermittelt zwischen der Domäne und Datenmapping- Schichten unter Verwendung eine Sammlung ähnliche Schnittstelle für Domänenobjekte zugreifen.“

Mit anderen Worten, ein Repository behandelt auch Daten und verbirgt Abfragen, die DAO ähnlich sind. Es befindet sich jedoch auf einer höheren Ebene, näher an der Geschäftslogik einer App.

Folglich kann ein Repository ein DAO verwenden, um Daten aus der Datenbank abzurufen und ein Domänenobjekt zu füllen. Oder es kann die Daten aus einem Domänenobjekt vorbereiten und sie mithilfe eines DAO zur Persistenz an ein Speichersystem senden.

Lassen Sie uns eine einfache Implementierung des Repository-Musters für die Benutzerdomäne untersuchen .

3.1. UserRepository

Zuerst erstellen wir die UserRepository- Oberfläche:

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

Hier haben wir einige gängige Methoden wie Abrufen , Hinzufügen , Aktualisieren und Entfernen hinzugefügt , um mit der Sammlung von Objekten zu arbeiten.

3.2. UserRepositoryImpl

Anschließend erstellen wir die UserRepositoryImpl- Klasse, die eine Implementierung der UserRepository- Schnittstelle bereitstellt:

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); } // ... }

Hier haben wir UserDaoImpl verwendet , um Daten aus der Datenbank zu senden / abzurufen.

Bisher können wir sagen, dass die Implementierungen von DAO und Repository sehr ähnlich aussehen, da die Benutzerklasse eine anämische Domäne ist. Ein Repository ist nur eine weitere Schicht über der Datenzugriffsschicht (DAO).

DAO scheint jedoch ein perfekter Kandidat für den Zugriff auf die Daten zu sein, und ein Repository ist ein idealer Weg, um einen Geschäftsanwendungsfall zu implementieren .

4. Repository-Muster mit mehreren DAOs

Um die letzte Aussage klar zu verstehen, erweitern wir unsere Benutzerdomäne , um einen geschäftlichen Anwendungsfall zu behandeln.

Stellen Sie sich vor, wir möchten ein Social-Media-Profil eines Benutzers erstellen, indem wir seine Twitter-Tweets, Facebook-Beiträge und mehr zusammenfassen.

4.1. Tweet

Zuerst erstellen wir die Tweet- Klasse mit einigen Eigenschaften, die die Tweet-Informationen enthalten:

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

4.2. TweetDao und TweetDaoImpl

Anschließend erstellen wir ähnlich wie bei UserDao die TweetDao- Oberfläche, über die Tweets abgerufen werden können:

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

Ebenso erstellen wir die TweetDaoImpl- Klasse, die die Implementierung der fetchTweets- Methode bereitstellt :

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

Hier rufen wir Twitter-APIs auf, um alle Tweets eines Benutzers mithilfe seiner E-Mail abzurufen.

In diesem Fall bietet ein DAO einen Datenzugriffsmechanismus unter Verwendung von APIs von Drittanbietern.

4.3. Verbessern Benutzer Domain

Zuletzt erstellen wir die UserSocialMedia- Unterklasse unserer User- Klasse, um eine Liste der Tweet- Objekte zu führen:

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.