Book Lounge/도메인 주도 개발 시작하기
6장 - 응용 서비스와 표현 영역
고딩왕 코범석
2022. 7. 16. 21:04
Index
표현 영역과 응용 영역
표현 영역의 역할
- URL, 요청 파라미터, 쿠키, 헤더 등을 이용해 사용자가 실행하고 싶은 기능을 판별해 해당 기능을 제공하는 응용 서비스를 실행하는 역할
- 응용 서비스를 실행하기 위해 응용 서비스가 요구하는 형식으로 사용자 요청을 변경
- 실행 결과를 사용자에게 알맞은 형식으로 응답
- HTML, JSON …
- 응용 영역은 표현 영역에 의존하지 않아야 한다.
- 즉, 응용 영역은 사용자가 웹 브라우저를 호출하는지, REST API를 호출하는지, TCP 소켓을 사용하는지 알 필요가 없고 기능에 충실해야 한다.
응용 서비스의 역할
- 응용 서비스는 사용자의 요청을 처리하기 위해 리포지터리에서 도메인 객체를 가져와 사용한다.
- 즉, 응용 서비스는 표현 영역괃 도메인 영역을 연결해주는 창구 역할을 담당한다.
- 만약 응용 서비스 로직이 복잡하다면 응용 서비스에서 도메인 로직의 일부를 구현하고 있을 가능성이 높다.
- 응용 서비스가 도메인 로직을 일부 구현하면 코드 중복, 로직 분산 등 코드 품질에 안좋은 영향을 미칠 수 있다.
- 트랜잭션 처리도 응용 서비스가 담당한다.
- 한 도메인의 상태 변경을 트랜잭션으로 처리한다.
도메인 로직 넣지 않기
- 도메인 로직을 응용 서비스에 위치시키면 다음과 같은 문제들이 발생한다.
- 코드의 응집성이 떨어져 도메인 데이터와 데이터를 조작하는 도메인 로직이 한 곳에서 파악하기 힘들다.
- 이는 도메인의 로직을 파악하기 위해 여러 영역을 분석해야한다.
- 여러 응용 서비스에서 동일한 도메인 로직을 구현할 가능성이 높아진다.
- 코드의 응집성이 떨어져 도메인 데이터와 데이터를 조작하는 도메인 로직이 한 곳에서 파악하기 힘들다.
- 이 두 가지 문제는 코드 변경을 어렵게 만든다.
- 소프트웨어의 중요한 경쟁력은 변경 용이성인데, 이를 높이려면 도메인 로직을 도메인 영역에 모아 코드 중복을 줄이고 응집도를 높여야한다.
응용 서비스의 구현
- 응용 서비스는 표현 영역과 도메인 영역을 연결하는 매개체 역할을 담당하는데, 이는 디자인 패턴에서 파사드와 같은 역할을 담당한다.
응용 서비스의 크기
- 응용 서비스에 대한 크기를 따져보기 전 응용 서비스를 어떻게 만들것인가는 다음과 같은 방법이 있다.
- 도메인에 해당하는 서비스를 한 클래스 내에서 구현하기
- 기능별 서비스 클래스를 만들기
- 1번 방법의 경우에 대한 장단점은 다음과 같다.
- 동일한 코드에 대해 중복을 제거할 수 있다는 장점이 있다.
- 예를 들어, 여러 메서드에 한 도메인 객체를 찾는 로직이 있다면 이를 private 메서드로 추출하여 중복을 줄일 수 있다.
- 하지만 클래스의 크기가 커진다는 점이 단점이다.
- 코드 크기가 커지면 연관 없는 코드들이 한 클래스에 몰릴 가능성이 높다. 이는 코드의 가독성을 저해시킨다.
- 동일한 코드에 대해 중복을 제거할 수 있다는 장점이 있다.
- 2번 방법의 경우에 대한 장단점은 다음과 같다.
- 1번 방법에 비해 코드 품질을 일정 수준으로 유지하는데 도움이 된다. 또한 각 클래스별로 필요한 의존 객체만 포함하므로 다른 기능을 구현한 코드에 대해 영향을 받지 않는다.
- 클래스의 갯수가 많아진다는 단점이 발생한다.
응용 서비스의 인터페이스와 클래스
- 인터페이스는 구현체가 여러 개일 경우와 런타임에 구현 객체를 변경해야할 때 유용하다.
- 하지만 응용 서비스는 런타임에 교체되는 경우가 거의 없고, 구현 클래스가 여러 개인 경우도 드물다.
- TDD로 표현 영역부터 개발한다면 인터페이스를 만들고 Mockito를 이용해 인터페이스에 Mock 객체를 주입시킬 수 있어 용이하다. 하지만 Mockito는 클래스에 대한 대역 객체도 생성할 수 있기 때문에 응용 서비스에 대한 인터페이스의 필요성을 약화시킨다.
메서드 파라미터와 값 리턴
- 스프링 MVC에서는 웹 요청 파라미터를 자바 객체로 변환하는 기능을 제공하므로 응용 서비스에 데이터로 전달할 요청 파라미터가 두 개 이상 존재하면 데이터 전달을 위한 별도의 클래스를 만드는 것이 좋다.
- 응용 서비스에서 애그리거트 자체를 리턴하면 편리함이 장점이지만 도메인의 로직 실행을 응용 서비스와 표현 영역 두 곳에서 조작할 수 있게 된다. 이는 응집도를 낮추는 원인이 된다.
- 응용 서비스에서는 표현 영역에서 필요한 데이터만 리턴하는 것이 기능 실행 로직의 응집도를 확실히 올리는 방법이다.
표현 영역에 의존하지 않기
HttpServletRequest
나HttpSession
을 응용 서비스에 파라미터로 전달하면 안 된다. 이유는 다음과 같다.- 응용 서비스에서 표현 영역에 대한 의존이 발생하면 응용 서비스만 테스트하기 어려워진다.
- 표현 영역의 구현이 변경되면 응용 서비스에 대한 변경도 발생한다.
HttpSession
과 쿠키는 표현 영역의 상태에 해당하는데 이 상태를 응용 서비스에서 변경해버리면 표현 영역의 코드만으로 표현 영역의 상태가 어떻게 변경되는지에 대해 파악이 어려워진다.
트랜잭션 처리
- 스프링에서 제공하는
@Transactional
을 사용하면 간단한 설정만으로 트랜잭션 코드를 수행하고RuntimeException
이 발생하면Rollback
이 발생한다. - 코드 또한 간단해지는 이점이 있다.
표현 영역
표현 영역의 책임
- 사용자가 시스템을 사용할 수 있는 흐름(화면)을 제공하고 제어한다.
- 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 제공한다.
- 사용자의 세션을 관리한다.
- 웹은 쿠키나 서버 세션을 이용해 사용자의 연결 상태를 관리한다.
값 검증
- 값 검증은 표현 영역과 응용 영역에서 모두 수행할 수 있다.
- 표현 영역에서는 사용자의 입력 값이 잘못입력될 경우 이를 사용자에게 알려줘야하는 책임이 있다.
- 스프링 MVC에서는 이를 Errors와 BindingResult를 통해 해결한다.
- 응용 서비스는 정상적인 입력 값을 받고 수행 도중 논리상의 이유로 맞지 않아 예외를 반환해야하는 책임을 가지고 있다.
- 정리해보자면 표현 영역과 응용 서비스 영역은 다음과 같은 검증 책임을 갖고 있다.
- 표현 영역
- 필수 값, 값의 형식, 값의 범위 검증
- 응용 서비스 영역
- 데이터의 존재에 대한 유무, 이외의 논리적인 비즈니스 오류
- 표현 영역
- 각 영역마다 검증을 하게 되면 예외 상황에 대한 작성 코드량이 늘어나지만 응용 서비스의 완성도가 높아지는 장점이 있다.
권한 검사
권한 검사가 수행되어야 하는 곳?
- 우선 권한 검사는 다음과 같은 영역에서 이뤄질 수 있다.
- 표현 영역
- 응용 서비스
- 도메인
- 표현 영역에서의 권한 검사는 인증된 사용자인지 아닌지 검사하는 것이다. 이는 주로 서블릿 필터에서 검증한다.
- 응용 서비스에서의 권한 검사는 스프링의 AOP를 활용해 어노테이션으로 서비스 메서드에 대한 권한 검사를 수행한다.
- 개별 도메인에서의 권한 검사가 복잡한데, 응용 서비스 수준에서 검사할 수 없다면 응용 서비스 내 로직에서 애그리거트를 불러와야 개별 도메인에서 권한을 검사할 수 있다. 이 때는 도메인 로직에 권한 체크 로직을 별도로 구현해야한다.
조회 전용 기능과 응용 서비스
- 조회 기능에서 별도의 트랜잭션이 필요없거나 DAO를 통해 데이터를 조회하는 것 외에 별다른 로직이 없다면 서비스를 만들지 않고 표현 영역에서 바로 조회 전용 기능을 사용해도 문제가 없다.
- 즉, 응용 서비스가 사용자의 요청 기능을 실행시키는데 별다른 기여를 하지 않는다면 굳이 구현할 이유는 없다.