Web, Service, Dao 레이어를 분리해둔 코드에 대해, 현재 database를 사용하지 않는데 레이어 분리가 꼭 필요했을지에 대해 질문 받았다.
Dao 레이어 분리
정확하겐 아래와 같이 레이어를 분리해뒀다.
•
Web : request/responseDTO로 웹과 상호작용
•
Service : request/responseDTO를 domain으로 매핑해 웹과 데이터저장공간 사이를 연결
•
Dao : domain으로 데이터저장공간과 상호작용
이때, Dao는 아래와 같은 꼴이었다. 후에 save, delete, find 동작이 List 콜렉션이 아닌 database 를 대상으로 동작하게 리팩토링 할 예정이었다.
@Component
public class ReservationMemoryDao implements ReservationDao {
private final List<Reservation> reservations = new ArrayList<>();
private final AtomicLong counter = new AtomicLong();
@Override
public List<Reservation> findAll() {
return reservations;
}
@Override
public Optional<Reservation> findById(Long id) {
return reservations.stream()
.filter(reservation -> reservation.getId().equals(id))
.findFirst();
}
@Override
public void save(Reservation reservation) {
reservation.setId(counter.incrementAndGet());
reservations.add(reservation);
}
@Override
public void delete(Reservation reservation) {
reservations.remove(reservation);
}
}
Java
복사
꼭 database가 아닌 메모리 내 데이터저장공간을 대상으로도 레이어 분리는 의미가 있다 생각했다. List 같은 자료구조를 사용하여 데이터를 관리하는 경우에도, 작성해둔 각 레이어의 역할이 퇴색되지 않기 때문이다.
특히 Service 레이어를 넣을지 말지에 대해서 고민을 좀 했었는데, 메모리 내 데이터저장공간을 memoryDao로 다루기로 한 이상 dao와 web 사이를 연결해주는 중간 계층(Service)을 넣는 편이 코드를 간결하게 유지하는데 도움이 된다 판단했다.
오버엔지니어링과 Dao
오버엔지니어링은 현재 필요하지 않은 기능을 미리 고려하는 것이라 생각한다. 만일 현재 사용하지 않는 database만을 위한 계층분리였다면 오버엔지니어링이었겠지만, 현재 기능에서 필요로 하는 데이터저장공간(List)을 위한 계층분리이기에 오버엔지니어링이 아니라 생각했다.
허나, 무언가 DB와 같이 저장한다 라는 생각을 무심코 했단 걸 이내 깨달았다. List<Reservation>을 Dao가 접근할 데이터저장소로 보는 건 가능하지만, 꼭 데이터저장소로 보아야 할까라는 고민을 해보니 현재 시점에서 그럴 필요가 없었다.
ReservationDao가 아닌 Reservations 라는 이름을 붙여 List<Reservation>을 필드로 가지는 일급컬렉션으로 사용할 수도 있기 때문이다. 웹 개발이 아니었다면 일급컬렉션으로 이를 표현했을 것 같다. 웹 개발에 너무 익숙해져 관성적으로 분리한 부분이었단 걸 깨달았고, 오버엔지니어링을 앞으론 좀 더 면밀히 고려해봐야겠다고 생각했다.
Dao에 대한 오해: 영속성
이때, List<Resrvation>을 Dao가 접근할 데이터저장소로 보는 게 가능한가에 대해 의문이 드는 이도 있을 것이다. 나 역시 그러했다. 따라서, ReservationMemoryDao 클래스를 ReservationMemoryRepository 로 이름을 변경할지 고민했었다. 그 이유는 Dao는 영속성과 관련되고, Repository는 도메인과 관련된다고 알고 있었는데 현재 코드의ReservationMemoryDao는 영속성을 가진 저장소에 접근하고 있지 않았기 때문이다.
결론부터 말하자면 공식문서를 찾아보며 Dao가 꼭 영속성과 관련될 필욘 없단 점을 깨달아, 해당 클래스를 표현할 때 Dao와 Repository 중엔 Dao가 더 적합한 네이밍이란 결론을 내렸다.
실제로 어떤 사람들은 Dao는 영속성 메커니즘에 접근해 CRUD를 처리하는 패턴이라 정의내리기도 한다. Data Access Object라는 이름 자체가 persistence를 나타낸다고 하는 블로그 글도 보았는데 공감하기 어려웠다. 그래서 최대한 공식문서에서 말하는 Dao에 대해 찾아보게 되었다.
Oracle에서 Dao에 대해 아래와 같이 설명한다. 이를 통해 Dao에서 접근할 데이터 저장소가 꼭 영속성을 가진 대상일 필요는 없음을 알 수 있다. ReservationMemoryDao 가 저장소로 바라보고 있는 List가 Dao의 데이터 저장소로 손색없다는 뜻이다.
A data access object can represent data that is not stored in a database. The sample application uses the DAO pattern to represent XML data sources as objects. Sample application screens are defined in an XML file which is interpreted by the class ScreenDefinitionDAO.