1. 개요

종종 리포지토리와 DAO의 구현은 특히 데이터 중심 앱에서 상호 교환 가능한 것으로 간주됩니다. 이것은 그들의 차이점에 대한 혼란을 만듭니다.

이 기사에서는 DAO와 리포지토리 패턴의 차이점에 대해 설명합니다.

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. 유저다오

그런 다음 사용자 도메인 에 대한 간단한 CRUD 작업을 제공 하는 UserDao 인터페이스를 만듭니다.

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

2.3. UserDaoImpl

마지막으로 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 인터페이스 를 사용하여 기본 스토리지와 상호 작용하고 사용자 도메인에 대한 데이터 액세스 메커니즘을 제공했습니다.

3. 리포지토리 패턴

Eric Evans의 저서 Domain-Driven Design 에 따르면 " 리포지토리는 개체 모음을 에뮬레이트하는 저장, 검색 및 검색 동작을 캡슐화하는 메커니즘입니다."

마찬가지로 Patterns of Enterprise Application Architecture 에 따르면 "도메인 개체에 액세스하기 위한 컬렉션과 같은 인터페이스를 사용하여 도메인과 데이터 매핑 계층 사이를 중재합니다."

즉, 리포지토리는 DAO와 유사하게 데이터를 처리하고 쿼리를 숨깁니다. 그러나 앱의 비즈니스 로직에 더 가까운 더 높은 수준에 있습니다.

결과적으로 저장소는 DAO를 사용하여 데이터베이스에서 데이터를 가져오고 도메인 개체를 채울 수 있습니다. 또는 도메인 개체에서 데이터를 준비하고 지속성을 위해 DAO를 사용하여 스토리지 시스템으로 보낼 수 있습니다.

사용자 도메인 에 대한 리포지토리 패턴의 간단한 구현을 살펴보겠습니다 .

3.1. 사용자 저장소

먼저 UserRepository 인터페이스를 생성해 보겠습니다.

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

여기에서 get , add , updateremove 와 같은 몇 가지 일반적인 메서드를 추가 하여 객체 컬렉션을 사용했습니다.

3.2. UserRepositoryImpl

그런 다음 UserRepository 인터페이스 구현을 제공하는 UserRepositoryImpl 클래스를 만듭니다.

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 을 사용하여 데이터베이스에서 데이터를 전송/검색했습니다.

지금까지 우리는 User 클래스가 빈약한 도메인 이기 때문에 DAO와 저장소의 구현이 매우 유사하다고 말할 수 있습니다. 그리고 리포지토리는 데이터 액세스 계층(DAO) 위의 또 다른 계층입니다.

그러나 DAO는 데이터에 액세스할 수 있는 완벽한 후보로 보이며 리포지토리는 비즈니스 사용 사례를 구현하는 이상적인 방법 입니다.

4. 여러 DAO가 있는 리포지토리 패턴

마지막 문장을 명확하게 이해하기 위해 사용자 도메인을 개선하여 비즈니스 사용 사례를 처리해 보겠습니다.

Twitter 트윗, Facebook 게시물 등을 집계하여 사용자의 소셜 미디어 프로필을 준비한다고 상상해 보십시오.

4.1. 트위터

먼저 트윗 정보를 보유하는 몇 가지 속성을 사용하여 Tweet 클래스를 만듭니다.

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

    // getters and setters
}

4.2. TweetDaoTweetDaoImpl

그런 다음 UserDao 와 유사하게 트윗 을 가져올 수 있는 TweetDao 인터페이스를 만듭니다 .

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

마찬가지로, fetchTweets 메서드 의 구현을 제공하는 TweetDaoImpl 클래스를 생성합니다.

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를 사용하여 데이터 액세스 메커니즘을 제공합니다.

4.3. 사용자 도메인 강화

마지막으로 User 클래스의 UserSocialMedia 하위 클래스를 만들어 트윗 개체 List을 유지해 보겠습니다.

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

    // getters and setters
}

여기에서 UserSocialMedia 클래스는 사용자 도메인의 속성도 포함하는 복잡한 도메인입니다.

4.4. UserRepositoryImpl

이제 UserRepositoryImpl 클래스를 업그레이드 하여 트윗 List과 함께 사용자 도메인 개체를 제공합니다.

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 게시물 List을 유지하도록 사용자 도메인을 향상시킬 수 있습니다.

5. 두 패턴 비교

이제 DAO 및 리포지토리 패턴의 뉘앙스를 살펴보았으므로 차이점을 요약해 보겠습니다.

  • DAO는 데이터 지속성의 추상화입니다. 그러나 리포지토리는 개체 컬렉션의 추상화입니다.
  • DAO는 스토리지 시스템에 더 가까운 저수준 개념입니다. 그러나 리포지토리는 도메인 개체에 더 가까운 상위 수준 개념입니다.
  • DAO는 데이터 매핑/액세스 계층으로 작동하여 못생긴 쿼리를 숨깁니다. 그러나 리포지토리는 도메인과 데이터 액세스 계층 사이의 계층으로, 데이터를 수집하고 도메인 개체를 준비하는 복잡성을 숨깁니다.
  • DAO는 저장소를 사용하여 구현할 수 없습니다. 그러나 저장소는 기본 저장소에 액세스하기 위해 DAO를 사용할 수 있습니다.

또한 빈약한 도메인이 있는 경우 리포지토리는 DAO일 뿐입니다.

또한 리포지토리 패턴은 도메인 기반 설계를 장려하여 비기술 팀 구성원도 데이터 구조를 쉽게 이해할 수 있도록 합니다 .

6. 결론

이 기사에서는 DAO와 리포지토리 패턴의 차이점을 살펴보았습니다.

먼저 DAO 패턴의 기본 구현을 조사했습니다. 그런 다음 Repository 패턴을 사용하여 유사한 구현을 보았습니다.

마지막으로 비즈니스 사용 사례를 해결하기 위해 도메인의 기능을 향상시키는 여러 DAO를 활용하는 리포지토리를 살펴보았습니다.

따라서 앱이 데이터 중심에서 비즈니스 지향으로 이동할 때 리포지토리 패턴이 더 나은 접근 방식임을 입증할 수 있습니다.

평소와 같이 모든 코드 구현은  GitHub에서 사용할 수 있습니다 .

Persistence footer banner