«1. Обзор

Часто реализации репозитория и DAO считаются взаимозаменяемыми, особенно в приложениях, ориентированных на данные. Это создает путаницу в их различиях.

В этой статье мы обсудим различия между шаблонами DAO и Repository.

2. Шаблон DAO

Шаблон объекта доступа к данным, также известный как шаблон DAO, представляет собой абстракцию сохраняемости данных и считается более близким к базовому хранилищу, которое часто ориентировано на таблицы.

Поэтому во многих случаях наши DAO соответствуют таблицам базы данных, что позволяет более просто отправлять/извлекать данные из хранилища, скрывая уродливые запросы.

Давайте рассмотрим простую реализацию шаблона DAO.

2.1. Пользователь

Во-первых, давайте создадим базовый класс домена пользователя:

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

    // getters and setters
}

2.2. UserDao

Затем мы создадим интерфейс UserDao, обеспечивающий простые операции CRUD для пользовательского домена:

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

2.3. UserDaoImpl

Наконец, мы создадим класс 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);
    }

    // ...
}

Здесь для простоты мы использовали интерфейс JPA EntityManager для взаимодействия с базовым хранилищем и предоставления механизма доступа к данным для Пользовательский домен.

3. Шаблон репозитория

Согласно книге Эрика Эванса «Domain-Driven Design», «репозиторий — это механизм для инкапсуляции поведения хранения, извлечения и поиска, который эмулирует набор объектов».

Аналогичным образом, в соответствии с Patterns of Enterprise Application Architecture, он «является посредником между уровнями отображения домена и данных, используя интерфейс, подобный коллекции, для доступа к объектам домена».

Другими словами, репозиторий также имеет дело с данными и скрывает запросы. похоже на ДАО. Однако он находится на более высоком уровне, ближе к бизнес-логике приложения.

Следовательно, репозиторий может использовать DAO для извлечения данных из базы данных и заполнения объекта домена. Или он может подготовить данные из объекта домена и отправить их в систему хранения, используя DAO для сохранения.

Давайте рассмотрим простую реализацию шаблона репозитория для пользовательского домена.

3.1. UserRepository

Во-первых, давайте создадим интерфейс UserRepository:

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

Здесь мы добавили несколько общих методов, таких как get, add, update и remove для работы с коллекцией объектов.

3.2. UserRepositoryImpl

Затем мы создадим класс 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 кажется идеальным кандидатом для доступа к данным, а репозиторий — идеальным способом реализации бизнес-прецедента.

4. Шаблон репозитория с несколькими DAO

Чтобы лучше понять последнее утверждение, давайте усовершенствуем наш домен пользователя для обработки бизнес-варианта.

Представьте, что мы хотим подготовить профиль пользователя в социальной сети, объединив его твиты в Твиттере, посты в Фейсбуке и многое другое.

4.1. Tweet

Во-первых, мы создадим класс Tweet с несколькими свойствами, которые содержат информацию о твитах:

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

    // getters and setters
}

4.2. TweetDao и TweetDaoImpl

Затем, аналогично 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;
    }
}

Здесь мы вызовем API Twitter, чтобы получить все твиты пользователя, использующего его электронную почту.

Итак, в этом случае DAO предоставляет механизм доступа к данным с использованием сторонних API.

4.3. Расширение пользовательского домена

Наконец, давайте создадим подкласс UserSocialMedia нашего класса User, чтобы хранить список объектов Tweet:

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

    // getters and setters
}

Здесь наш класс UserSocialMedia представляет собой сложный домен, содержащий также свойства пользовательского домена.

4.4. UserRepositoryImpl

Теперь мы обновим наш класс UserRepositoryImpl, чтобы предоставить объект домена пользователя вместе со списком твитов:

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 для доступа к данным из различных источников.

Точно так же мы можем улучшить наш домен пользователя, чтобы хранить список сообщений Facebook.

5. Сравнение двух шаблонов

    Теперь, когда мы рассмотрели нюансы шаблонов DAO и Repository, давайте суммируем их различия:

DAO — это абстракция сохраняемости данных. Однако репозиторий — это абстракция набора объектов. DAO — это концепция более низкого уровня, более близкая к системам хранения. Однако репозиторий — это концепция более высокого уровня, более близкая к объектам предметной области. DAO работает как слой отображения/доступа к данным, скрывающий уродливые запросы. Однако репозиторий — это слой между доменами и уровнями доступа к данным, скрывающий сложность сопоставления данных и подготовки объекта домена. DAO не может быть реализован с использованием репозитория. Однако репозиторий может использовать DAO для доступа к базовому хранилищу

Кроме того, если у нас анемичный домен, репозиторий будет просто DAO.

Кроме того, шаблон репозитория поощряет дизайн, ориентированный на предметную область, обеспечивая легкое понимание структуры данных и для нетехнических членов команды.

6. Заключение

В этой статье мы рассмотрели различия между шаблонами DAO и Repository.

Сначала мы рассмотрели базовую реализацию шаблона DAO. Затем мы увидели аналогичную реализацию с использованием шаблона Repository.

Наконец, мы рассмотрели репозиторий, использующий несколько DAO, расширяющий возможности домена для решения бизнес-прецедентов.

Таким образом, мы можем сделать вывод, что шаблон репозитория является лучшим подходом, когда приложение переходит от ориентированного на данные к бизнес-ориентированному.