6. 응용 서비스와 표현 영역
#
표현 영역과 응용 영역- 1~5장까지는 도메인과 구현에 대한 내용이였습니다.
- 도메인 영역을 잘 구현하지 못하면 사용자 요구를 충족하는 소프트웨어를 만들지 못합니다.
- 도메인이 제 기능을 하려면 사용자와 도메인 영역을 연결해주는 매개체 역할이 필요합니다.
- 표현 영역은 사용자의 요청을 해석합니다.
- ex. 웹 브라우저의 URL, 요청 파라미터, 쿠키, 헤더 등
- 응용 서비스의 메서드가 요구하는 파라미터와 표현 영역이 사용자로부터 전달받은 데이터는 형식이 일치하지 않기 때문에 표현 영역을 응용 서비스가 요구하는 형식으로 사용자 요청을 변환합니다.
- 사용자와의 상호작용은 표현 영역이 처리하기 때문에 응용 서비스는 표현 영역에 의존하지 않습니다.
#
응용 서비스의 역할- 응용 서비스는 사용자가 요청한 기능을 실행합니다.
- 응용 서비스는 사용자의 요청을 처리하기 위해 리포지터리로부터 도메인 객체를 구하고, 도메인 객체를 사용합니다.
- 응용 서비스의 주요 역할은 도메인 객체를 사용해서 사용자의 요청을 처리하는 것이므로 사용자 영역 입장에서 보았을 때 파사드(facade) 역할을 합니다.
- 응용 서비스는 주로 도메인 객체 간의 흐름을 제어하기 때문에 다음과 같이 단순한 형태를 갖습니다.
- 응용 서비스의 주된 역할은 도메인 객체 간의 실행 흐름을 제어하는 것과 트랜잭션 처리입니다.
#
도메인 로직 넣지 않기- 도메인 로직은 도메인 영역에 위치하고 응용 서비스는 도메인 로직을 구현하지 않습니다.
- 암호 변경 기능을 위한 응용 서비스는 도메인 객체 간의실행 흐름을 제어합니다.
- 도메인 로직을 도메인 영역과 응용 서비스에 분산해서 구현하면 코드 품질에 문제가 발생합니다.
- 첫번째는 코드의 응집성이 떨어집니다.
- 두번째는 여러 응용 서비스에서 동일한 도메인 로직을 구현할 가능성이 높아집니다.
- 위 두가지 문제는 결과적으로 코드 변경을 어렵게합니다.
- 소프트웨어 가치를 높이려면 도메인 로직을 도메인 영역에 모아서 코드 중복이 발생하지 않도록 하고 응집도를 높여야 합니다.
#
응용 서비스의 구현- 응용 서비스는 표현 영역과 도메인 영역을 연결하는 매개체 역할을 하는데 이는 디자인 패턴에서 파사드(facade)와 같은 역할을 합니다.
#
응용 서비스의 크기- 응용 서비스 자체의 구현은 어렵지 않으나 고민해야하는 부분이 있으며 대표적인 예시가 응용 서비스의 크기입니다.
- 회원에 대한 기능을 구현할 때는 대표적으로 다음 방범 중 한가지 방법을 사용합니다.
- 한 응용 서비스 클래스에 회원 도메인의 모든 기능 구현하기
- 구분되는 기능별로 응용 서비스 클래스를 따로 구현하기
- 한 도메인에 관련된 기능을 구현한 코드가 한 클래스에 위치하므로 각기능에서 동일 로직에 대한 코드 중복을 제거할 수 있다는 장점이 있습니다.
- 각 기능에서 동일한 로직을 위한 코드 중복을 제거하는 것이 쉽다는 것이 장점이라면 한 서비스 클래스의 크기가 커진다는 것은 단점입니다.
- 한 클래스에 코드가 모이기 시작하면 코드를 점점 얽히게 만들어 코드 품질이 낮추는 결과를 초래합니다.
- 각 기능마다 동일한 로직을 구현할 경우, 여러 클래스에 중복해서 동일한 코드를 구현할 가능성이 있습니다.
- 한 도메인과 관련된 기능을 하나의 응용 서비스 클래스에서 보두 구현하는 방식보다 구분되는 기능을 별도의 서비스 클래스로 구현하는 방식을 사용합니다.
#
응용 서비스의 인터페이스와 클래스- 응용 서비스를 구현할 때 논쟁이 될 만한 것은 인터페이스가 필요한지 여부입니다.
- 인터페이스가 필요한 몇가지 상황은 구현 클래스가 여러 개인 경우입니다.
- 인터페이스와 클래스를 따로 구현하면 소스 파일만 많아지고 구현 클래스에 대한 간접 참조가 증가해서 전체 구조만 복잡해지는 문제가 발생합니다.
#
메서드 파라미터와 값 리턴- 응용 서비스가 제공하는 메서드는 이용해서 사용자가 요구한 기능을 실행하는데 필요한 값을 파라미터를 통한 전달받아야합니다.
- 응용 서비스는 데이터 클래스를 파라미터로 전달받고 필요한 데이터를 추출해서 필요한 기능을 구현하면 됩니다.
- 응용 서비스는 표현 영역에서 필요한 데이터만 리턴하는 것이 기능 실행 로직의 응집도를 높이는 확실히 방법입니다.
#
표현 영역에 의존하지 않기- 응용 서비스의 파라미터 타입을 결정할 때 주의할 점은 표현 영역과 관련된 타입을 사용하면 안 된다는 점입니다.
- 응용 서비스에서 표현 영역에 대한 의존이 발생하면 응용 서비스만 단독으로 테스트하기가 어려워집니다.
- 응용 서비스가 표현 영역의 기술을 사용하지 않도록 해야합니다.
- 서비스 메서드의 파라미터와 리턴 타입으로 표현 영역의 구현 기술을 사용하지 않는 것입니다.
#
트랜잭션 처리- 프레임워크가 제공하는 트랜잭션 기능을 적극 사용하는 것이 좋습니다.
#
도메인 이벤트 처리- 응용 서비스의 역할 중 하나는 도메인 영역에서 발생시킨 이벤트를 처리하는 것입니다.
- 도메인에서 이벤트를 발생시키면 그 이벤트를 받아서 처리할 코드가 필요하며, 그 역할을 하는 것이 응용 서비스입니다.
- 이벤트를 사용하면 코드가 복잡해지는 대신 도메인 간의 의존성이나 외부 시스템에 대한 의존을 낮춰주는 장점을 얻을 수 있습니다.
#
표현 검증- 표현 영역의 책임은 다음과 같습니다.
- 사용자가 시스템을 사용할 수 있는 흐름을 제공하고 제어합니다.
- 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공합니다.
- 사용자의 세션을 관리합니다.
- 표현 영역의 첫 번째 책임은 사용자가 시스템을 사용할 수 있도록 알맞은 흐름을 제공하는 것입니다.
- 표현 영역의 두 번째 책임은 사용자의 요청을 받은 응용 서비스에 기능 실행을 요청하는 것입니다.
- 표현 영역의 주된 역할은 사용자의 연결 상태인 세션을 관리하는 것입니다.
#
값 검증- 값 검증은 표현 영역과 응용 서비스 두 곳에서 모두 수행할 수 있습니다.
- 원칙적으로 모든 값에 대한 검증은 응용 서비스에서 처리합니다.
- 응용 서비스에서 각 값이 존재하는지 형식이 올바른지 확인할 목적으로 익셉션을 사용할 대의 문제점을 사용자에게 좋지 않은 경험을 제공합니다.
- 응용 서비스를 사용하는 표현 영역 코드가 한 곳이면 구현의 편리함을 위해 다음과 같이 역할을 나누어 검증을 수행할 수도 있습니다.
- 표현 영역 : 필수 값, 값의 형식, 범위 등을 검증합니다.
- 응용 서비스 : 데이터의 존재 유무와 같은 논리적 오류를 검증합니다.
#
권한 검사- 개발할 시스템마다 권한의 복잡도가 달라집니다.
- 보안 프레임워크의 복잡도를 떠나 보통 다음의 세 곳에서 권한 검사를 수행할 수 있습니다.
- 표현 영역
- 응용 서비스
- 도메인
- 표현 영역에서 할 수 있는 가장 기본적인 검사는 인증된 사용자인지 아닌지 여부를 검사하는 것입니다.
- 이 URL을 처리하는 컨트롤러에 웹 요청을 전달하기 전에 인증 여부를 검사해서 인증된 사용자의 웹 요청만 컨트롤러에 전달합니다.
- 인증된 사용자가 아닐 경우 로그인 화면으로 리다이렉트시킵니다.
- 이런 접근 제어를 하기에 좋은 위치가 서블릿 필터입니다.
- URL 만으로 접근 제어를 할 수 없는 경우 응용 서비스의 메서드 단위로 권한 검사를 수행해야 합니다.
- 스프링 시큐리티와 같은 보안 프레임워크를 확장해서 개별 도메인 객체 수준의 권한 검사 기능을 프레임워크에 통합할 수 도 있습니다.
#
조회 전용 기능과 응용 서비스- 조회 기능과 응용 서비스에 대한 것입니다.
- 응용 서비스가 존재해야 한다는 강박관념을 가면, 컨트롤러와 같은 표현 영역에서 응용 서비스 없이 조회 전용 기능이나 도메인 리포지터리에 접근하는 것이 처음에는 이상하게 느껴질 수 있습니다.