Repository를 인터페이스화 하여 얻는 장점은 뭘까? 특히나, db가 바뀔 일 없는 상황에선 더더욱 의문이 들 수 있다. 이에 대해 내 나름대로의 근거를 찾아간 과정을 공유해보겠다.
Repository의 구현체를 Dao로 명명
우선 내 코드의 구조를 설명하자면 domain 패키지에 Repository 인터페이스가 들어가있고, Repository의 구현체는 dao 란 이름을 가진 채로 dao 패키지에 들어가있다. 이게 무슨 뚱딴지 같은 소리야? 싶은 이들은 코드로 좀 더 내 코드의 상황을 살펴보자. 이해를 돕기 위한 부분만 남기고 간략화한 코드다. 전체 코드를 보고 싶다면 아래 링크로 들어가서 볼 수 있다.
domain 패키지엔 아래 클래스들이 들어있다. 참고로, domain 패키지에 repository 인터페이스를 넣어준 이유는 repository를 도메인 객체의 컬렉션을 관리하는 개념으로 보았기 때문이다.
public class Reservation {
private Long id;
private String name;
private LocalDate date;
private ReservationTime time;
}
public interface ReservationRepository {
List<Reservation> findAll();
}
Java
복사
그리고 대망의 dao 패키지엔 아래 클래스가 들어있다. Repository의 구현체 이름을 Dao라 붙인 이유는 database에 접근하는 역할을 더 잘 표명할 수 있기 때문이다. 아래 클래스는 JdbcTemplate을 이용해 db에 쿼리를 날리고 있다.
@Repository
public class ReservationDao implements ReservationRepository {
private final JdbcTemplate jdbcTemplate;
private final RowMapper<Reservation> reservationRowMapper = (resultSet, rowNum) -> new Reservation(
resultSet.getLong("reservation_id"),
resultSet.getString("name"),
LocalDate.parse(resultSet.getString("date")),
new ReservationTime(
resultSet.getLong("time_id"),
LocalTime.parse(resultSet.getString("time_value"))
)
);
public ReservationDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public List<Reservation> findAll() {
String sql = "SELECT r.id AS reservation_id, r.name, r.date, t.id AS time_id, t.start_at AS time_value "
+ "FROM reservation AS r INNER JOIN reservation_time AS t ON r.time_id = t.id";
return jdbcTemplate.query(sql, reservationRowMapper);
}
}
Java
복사
Repository의 구현체를 다른 패키지에 둬서 얻는 장점
위와 같은 구조로 얻을 수 있는 장점으론 service가 dao 구현체를 아예 모르게 해줄 수 있단 점이 있다. 기존엔 service가 domain과 dao 패키지를 모두 살펴봤지만, 현재는 service가 domain 패키지만 알고, 구현체가 들어있는 dao 패키지는 모른다. 이를 통해 구현체를 숨길 수 있다. 그리고 지금과 같은 계층 구조에서 구현체를 숨기는 장점이 특히 두드라진다.
web / service(application service + domain service) / domain / database
크게 위의 계층 별로 패키지를 분리해뒀다. 인터페이스를 분리하지 않는다면, service는 domain과 database에 대해 모두 알아야 한다. 허나 인터페이스를 분리한다면, service는 domain만 알면 된다.
결국 web과 application service를 한 모듈로, domain service와 domain을 한 모듈로, database를 한 모듈로 바라볼 수 있다. 모듈 별로 사람을 나눠 개발하더라도 각 개발자들이 처음에 정한 인터페이스를 기반으로 독립적으로 개발할 수 있다. 작업의 효율성이 올라가는 것이다.
인터페이스를 분리함으로써 관리 포인트가 늘어난단 단점이 생길 수 있지만, 프로그램을 모듈 별로 나눠 개발할 수 있단 장점이 그 단점을 커버해줄 수 있다 생각한다. 현재 내가 진행하고 있는 페어프로그래밍 상황에도 새 페어가 database 계층에 대한 이해가 부족한 상태에서도 service와 domain 계층만 살펴보고 비즈니스 로직을 이해할 수 있을 것이다.
Service는 인터페이스화 하지 않은 이유
service의 경우 현재 application service 와 domain service 를 나눠 작업할만한 기능이 없어 나누지 않았다. 만일 web계층에서 외부 API를 사용한다던가 해서 web계층의 dto와 service 계층의 dto를 나눠 관리해야 할 포인트가 생긴다면 나눌 거 같다.