DAO vs Repository Patterns – DAO与存储库模式

最后修改: 2020年 9月 10日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Often, the implementations of repository and DAO are considered interchangeable, especially in data-centric apps. This creates confusion about their differences.

通常,存储库和DAO的实现被认为是可以互换的,特别是在以数据为中心的应用程序中。这就造成了对它们之间差异的混淆。

In this article, we’ll discuss the differences between DAO and Repository patterns.

在这篇文章中,我们将讨论DAO和Repository模式的区别。

2. DAO Pattern

2.DAO模式

The Data Access Object Pattern, aka DAO Pattern, is an abstraction of data persistence and is considered closer to the underlying storage, which is often table-centric.

数据访问对象模式,又称DAO模式是数据持久性的抽象,被认为更接近底层存储,而底层存储通常以表为中心

Therefore, in many cases, our DAOs match database tables, allowing a more straightforward way to send/retrieve data from storage, hiding the ugly queries.

因此,在许多情况下,我们的DAO与数据库表相匹配,允许以更直接的方式从存储中发送/检索数据,隐藏丑陋的查询。

Let’s examine a simple implementation of the DAO pattern.

让我们来看看DAO模式的一个简单实现。

2.1. User

2.1. 用户

First, let’s create a basic User domain class:

首先,让我们创建一个基本的User域类。

public class User {
    private Long id;
    private String userName;
    private String firstName;
    private String email;

    // getters and setters
}

2.2. UserDao

2.2. UserDao

Then, we’ll create the UserDao interface that provides simple CRUD operations for the User domain:

然后,我们将创建UserDao接口,为User域提供简单的CRUD操作。

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

2.3. UserDaoImpl

2.3UserDaoImpl

Last, we’ll create the UserDaoImpl class that implements the UserDao interface:

最后,我们将创建UserDaoImpl类,实现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);
    }

    // ...
}

Here, for simplicity, we’ve used the JPA EntityManager interface to interact with underlying storage and provide a data access mechanism for the User domain.

在这里,为了简单起见,我们使用了JPA EntityManager 接口来与底层存储交互,并为User域提供数据访问机制。

3. Repository Pattern

3.存储库模式

As per Eric Evans’ book Domain-Driven Design, the “repository is a mechanism for encapsulating storage, retrieval, and search behavior, which emulates a collection of objects.”

按照Eric Evans的书Domain-Driven Design“资源库是一种封装存储、检索和搜索行为的机制,它模拟了一个对象的集合。”

Likewise, according to Patterns of Enterprise Application Architecture, it “mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.”

同样,根据Patterns of Enterprise Application Architecture,它“在域和数据映射层之间进行调解,使用类似集合的接口来访问域对象”

In other words, a repository also deals with data and hides queries similar to DAO. However, it sits at a higher level, closer to the business logic of an app.

换句话说,资源库也处理数据,并隐藏了与DAO类似的查询。然而,它位于一个更高的层次,更接近应用程序的业务逻辑。

Consequently, a repository can use a DAO to fetch data from the database and populate a domain object. Or, it can prepare the data from a domain object and send it to a storage system using a DAO for persistence.

因此,存储库可以使用DAO从数据库获取数据并填充域对象。或者,它可以从域对象中准备数据,并使用DAO将其发送到存储系统中进行持久化。

Let’s examine a simple implementation of the Repository pattern for the User domain.

让我们来看看User域的Repository模式的一个简单实现。

3.1. UserRepository

3.1.UserRepository

First, let’s create the UserRepository interface:

首先,让我们创建UserRepository接口。

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

Here, we’ve added a few common methods like get, add, update, and remove to work with the collection of objects.

在这里,我们添加了一些常见的方法,如get, add, update, 和remove来处理对象的集合。

3.2. UserRepositoryImpl

3.2.UserRepositoryImpl

Then, we’ll create the UserRepositoryImpl class providing an implementation of the UserRepository interface:

然后,我们将创建UserRepositoryImpl类,提供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);
    }

    // ...
}

Here, we’ve used the UserDaoImpl to send/retrieve data from the database.

在这里,我们使用了UserDaoImpl来发送/检索数据库中的数据。

So far, we can say that the implementations of DAO and repository look very similar because the User class is an anemic domain. And, a repository is just another layer over the data-access layer (DAO).

到目前为止,我们可以说DAO和存储库的实现看起来非常相似,因为User类是一个贫乏的领域。而且,存储库只是数据访问层(DAO)上的另一层。

However, DAO seems a perfect candidate to access the data, and a repository is an ideal way to implement a business use-case.

然而,DAO似乎是访问数据的完美候选者,而资源库是实现业务用例的理想方式

4. Repository Pattern With Multiple DAOs

4.带有多个DAO的存储库模式

To clearly understand the last statement, let’s enhance our User domain to handle a business use-case.

为了清楚地理解最后一句话,让我们加强我们的User域来处理一个商业用例。

Imagine we want to prepare a social media profile of a user by aggregating his Twitter tweets, Facebook posts, and more.

想象一下,我们想通过聚合一个用户的Twitter推文、Facebook帖子等来准备一个用户的社交媒体资料。

4.1. Tweet

4.1.Tweet

First, we’ll create the Tweet class with a few properties that hold the tweet information:

首先,我们将创建Tweet类,该类有几个属性来保存tweet信息。

public class Tweet {
    private String email;
    private String tweetText;    
    private Date dateCreated;

    // getters and setters
}

4.2. TweetDao and TweetDaoImpl

4.2.TweetDaoTweetDaoImpl

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

然后,与UserDao类似,我们将创建TweetDao接口,允许获取tweets。

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

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

同样地,我们将创建TweetDaoImpl类,它提供了fetchTweets方法的实现。

public class TweetDaoImpl implements TweetDao {
    @Override
    public List<Tweet> fetchTweets(String email) {
        List<Tweet> tweets = new ArrayList<Tweet>();
        
        //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.

在这里,我们将调用Twitter的API来获取一个用户使用其电子邮件的所有推文。

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

所以,在这种情况下,DAO提供了一个使用第三方API的数据访问机制。

4.3. Enhance User Domain

4.3 增强用户

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

最后,让我们创建UserSocialMedia类的User子类,以保持Tweet对象的列表。

public class UserSocialMedia extends User {
    private List<Tweet> tweets;

    // getters and setters
}

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

在这里,我们的UserSocialMedia类是一个复杂的域,也包含User域的属性。

4.4. UserRepositoryImpl

4.4.UserRepositoryImpl

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

现在,我们将升级我们的UserRepositoryImpl 类,以提供一个User域对象和一个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<Tweet> 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.

这里,UserRepositoryImpl使用UserDaoImpl提取用户数据,使用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.

然后,它汇总这两组信息,并提供一个UserSocialMedia类的领域对象,这对我们的业务用例很方便。因此,资源库依赖DAO来访问各种来源的数据

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

同样地,我们可以加强我们的User域,以保持Facebook帖子的列表。

5. Comparing the Two Patterns

5.比较这两种模式

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

现在我们已经看到了DAO和Repository模式的细微差别,让我们总结一下它们的区别。

  • 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.

另外,如果我们有一个贫血的领域,资源库将只是一个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

6.结语

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

在这篇文章中,我们探讨了DAO和Repository模式之间的差异。

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

首先,我们研究了一个DAO模式的基本实现。然后,我们看到了一个使用存储库模式的类似实现。

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

最后,我们看了一个利用多个DAO的存储库,增强了一个领域的能力,解决了一个商业用例。

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.

像往常一样,所有的代码实现都可以在GitHub上找到