比较DAO与Repository存储库模式 - Baeldung


存储库和DAO的实现被认为是可互换的,尤其是在以数据为中心的应用程序中。这引起了他们之间差异的困惑。在本文中,我们将讨论DAO和存储库模式之间的区别。
 
DAO模式
数据访问对象模式(也称为DAO模式)是数据持久性的抽象,被认为更接近于以表为中心的基础存储。
因此,在许多情况下,我们的DAO与数据库表匹配,从而提供了一种更直接的方式来从存储发送/检索数据,从而隐藏了难看的查询。
让我们研究一下DAO模式的简单实现。
首先,让我们创建一个基本的User域类:

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

然后,我们将创建UserDao接口,该接口为User域提供简单的CRUD操作:
public interface UserDao {
    void create(User user);
    User read(Long id);
    void update(User user);
    void delete(String userName);
}

最后,我们将创建实现UserDao接口的UserDaoImpl类:
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);
    }
 
    // ...
}

在这里,为简单起见,我们使用了JPA EntityManager接口与基础存储进行交互,并为User域提供了一种数据访问机制。
 
Repository模式
也称存储库模式或仓储模式,根据Eric Evans的《领域驱动设计》一书,“存储库是一种封装存储,检索和搜索行为的机制,它模仿对象的集合。”
同样,根据企业应用程序体系结构的模式,它“使用类似集合的接口访问域对象,在域和数据映射层之间进行中介”。
换句话说,存储库还处理数据并隐藏类似于DAO的查询。但是,它处于更高的层次,更接近应用程序的业务逻辑。
因此,存储库可以使用DAO从数据库中获取数据并填充域对象。或者,它可以从域对象准备数据,并使用DAO将其发送到存储系统以实现持久性。
让我们看一下User域的Repository模式的简单实现。
首先,让我们创建UserRepository接口:
public interface UserRepository {
    User get(Long id);
    void add(User user);
    void update(User user);
    void remove(User user);
}

在这里,我们添加了一些通用方法,例如get,add,update和remove,以处理对象集合。
然后,我们将创建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);
    }
 
    // ...
}

在这里,我们使用了UserDaoImpl从数据库发送/检索数据。
到目前为止,我们可以说DAO和存储库的实现看起来非常相似,因为User类是贫血领域模型。而且,存储库只是数据访问层(DAO)之上的另一层。
但是,DAO似乎是访问数据的理想选择,而存储库是实现业务用例的理想方式。
 
具有多个DAO的存储库模式
为了清楚地理解最后一条语句,让我们增强用户域以处理业务用例。
想象一下,我们想通过汇总用户的Twitter微博,Facebook帖子等来准备用户的社交媒体资料。
首先,我们将创建Tweet类,其中包含一些包含tweet信息的属性:
public class Tweet {
    private String email;
    private String tweetText;    
    private Date dateCreated;
 
    // getters and setters
}

然后,类似于UserDao,我们将创建TweetDao接口,该接口允许获取推文:
public interface TweetDao {
    List<Tweet> fetchTweets(String email);    
}

同样,我们将创建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;
    }
}

在这里,我们将调用Twitter API,以使用户使用其电子邮件获取所有推文。
因此,在这种情况下,DAO提供了使用第三方API的数据访问机制。
最后,让我们创建User类的UserSocialMedia子类,以保留Tweet对象的列表:
public class UserSocialMedia extends User {
    private List<Tweet> tweets;
 
    // getters and setters
}

在这里,我们的UserSocialMedia类是一个复杂域,也包含User域的属性。
现在,我们将升级我们的UserRepositoryImpl类,以提供一个User域对象以及一条推文列表:
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;
    }
}

在这里,UserRepositoryImpl使用了UserDAOImpl和TweetDaoImpl。然后,它将汇总这两组信息并提供UserSocialMedia类的域对象,该类对象对于我们的业务用例非常方便。因此,存储库依赖于DAO来访问来自各种来源的数据。
 
比较两种模式
现在,我们已经了解了DAO和存储库模式的细微差别,让我们总结一下它们的区别:
  • DAO是数据持久性的抽象。但是,存储库是对象集合的抽象
  • DAO是一个较低层的概念,更接近于存储系统。但是,存储库是一个更高级的概念,更接近于域对象
  • DAO充当数据映射/访问层,隐藏了难看的查询。但是,存储库是域和数据访问层之间的一层,隐藏了整理数据和准备域对象的复杂性
  • DAO不能使用存储库来实现。但是,存储库可以使用DAO访问基础存储

另外,如果我们有一个贫血域,则存储库将只是一个DAO。
此外,存储库模式鼓励域驱动的设计,也使非技术团队成员也容易理解数据结构。
所有代码实现都可以  在GitHub上获得
 
banq注:该文没有Repostiory本质优点,其实一个DAO也可以组合另外两个DAO,这只是用法不同,取决于上下文,仓储模式最大区别是能够解耦业务模型对技术框架如持久层和数据库;而DAO则是简单将数据库数据表模型转为对象模型,对象模型屈从于数据表设计,而仓储模式则是倒过来,是围绕充血领域模型为主,防止充血模型耦合了数据库表。
参考:DDD中业务模型与框架等技术平台解耦的简单方法