DDD START SERIES는 도메인 주도 개발 시작하기 책을 참고 하여 작성된 요약 글 입니다.

6.1 표현 영역과 응용 영역

도메인이 제 기능을 하려면 사용자와 도메인을 연결해 주는 매개체가 필요하다.

응용 영역과 표현 영역이 사용자와 도메인을 연결해 주는 매개체 역할을 한다.

사용자 → 표현 영역 → 응용 영역 → 도메인 영역

표현 영역은 사용자의 요청을 해석한다.

해석 후 원하는 기능을 판별하여 기능을 제공하는 응용 서비스를 실행한다.

실제 사용자가 원하는 기능을 제공하는 것은 응용 영역에 위치한 서비스다.

ex) 사용자가 회원 가입을 요청했다면 실제 요청을 위한 기능을 제공하는 주체는 응용 서비스에 위치한다.

응용 서비스는 기능을 실행하는데 필요한 입력값은 메서드 인자로 받고 실행 결과를 리턴한다.

표현 영역은 응용 서비스가 요구하는 형식으로 사용자 요청을 변환한다.

6.2 응용 서비스의 역할

공용 서비스는 사용자가 요청한 기능을 실행한 그러기 위해 리포지터리에서 도메인 객체를 가져와 사용한다.

표현 영역 입장에선 도메인 영역과 연결해 주는 브릿지(응용 영역) 역할이다.

응용 서비스는 주로 도메인 객체 간의 흐름을 제어하기에 단순한 형태를 가진다.

ex) 
1. 애그리거트를 구한다. 
2. 애그리거트의 도메인 기능을 실행한다. 
3. 결과를 리턴한다.

만약 응용 서비스가 복잡하다면 응용 서비스에서 도메인 로직의 일부를 구현하고 있을 가능성이 높다.

응용 서비스가 도메인 로직을 구현하면 코드 중복, 로직 분산 등 코도 품질에 안 좋은 영향을 줄 수 있다.

또한 응용 서비스는 트랜잭션 처리도 담당하며 응용 서비스는 도메인의 상태 변경을 트랜잭션으로 처리해야 한다.

트랜잭션 외 주요 역할로 접근 제어이벤트 처리가 있다.

6.2.1 도메인 로직 넣지 않기

응용 서비스는 도메인 로직을 구현하면 안 된다.

도메인 로직은 도메인 영역과 응용 영역에 분산 구현하면 코드 품질에 다음과 같은 문제가 생긴다.

  1. 응집도가 떨어진다.

  2. 여러 응용 서비스에서 동일한 로직을 구현할 가능성이 높다.

이러한 문제점들은 응집도가 떨어지고 코드 중복이 발생하는 코드를 만들게 된다.

그럼 코드 변경이 어렵고 소프트웨어의 가치가 떨어지게 되는데 떨어지지 않고 가치를 높이기 위해선 위의 문제점들을 제외하고 구현하면 응집도를 높이고 코드의 중복을 막을 수 있다.

6.3 응용 서비스의 구현

응용 서비스는 표현 영역과 도메인 영역을 연결한 매개체 역할을 한다.(이는 디자인 패턴의 퍼사드와 같은 역할을 한다.)

응용 서비스는 복잡한 로직은 하지 수행하지 않기 때문에 구현은 어렵지 않다.

6.3.1 응용 서비스의 크기

응용 서비스는 구현이 어렵지 않지만 생각할게 몇 개 있는데 그중 하나가 응용 서비스의 크기다.

  • 응용 서비스 클래스에 도메인의 모든 기능 구현하기

  • 구분되는 기능별로 응용 서비스 클래스를 따로 구현하기

한 도메인 관련 기능을 하나의 클래스에 구현하면 동일한 로직에 대한 중복을 없앨 수 있다.

이게 장점이지만 단점은 클래스에 기능이 몰려 있어 클래스가 커질 수가 있다.

그럼 결국엔 연관성이 없는 코드끼리 뭉쳐서 코드가 보기 힘들 수 있다.

또한 한 클래스에 모든 기능을 구현하면 엄연히 분리하는 게 좋아 보임에도 습관적으로 기존 코드에 억지로 끼워 넣게 된다.

이것은 코드를 점점 얽히게 만들어 코드 품질을 낮춘다.

그렇기 때문에 구분되는 기능별로 서비스 클래스를 구현하는 방식은 한 응용 서비스 클래스에 한 개 내지 2~3개의 기능을 구현한다.

이 방식을 사용하면 클래스는 많아지지만 코드의 품질은 일정 수준 유지된다.

또한 각 클래스 별로 필요한 의존 객체만 포함하므로 다른 기능을 구현한 코도에 영향을 받지 않는다.

각 기능마다 동일한 로직을 구현할 경우 별도의 클래스를 구현해 사용할 수 있다.

ex)

public final userServiceHelper {
    public static User findBy(String name) {
        // ~~~~
    }
}

6.3.2 응용 서비스의 인터페이스와 클래스

인터페이스가 필요한 경우는 구현체가 여러 개인 경우인데 하지만 응용 서비스는 구현체가 두 개인 경우도 드물다.

이러한 이유로 인해 인터페이스와 구현체를 타고 구현하면 소스 파일만 많아지고 구현 클래스에 대한 간전 참조가 증가해 전체 구조가 복잡해지게 된다.

따라서 인터페이스가 명확하게 필요하기 전까지는 응용 서비스에 대한 인터페이스를 작성하는 것이 좋은 선택이라고 볼 수 없다.

6.3.3. 메서드 파라미터와 값 리턴

응용 서비스로의 값 전달에서 파라미터가 2개 이상인 경우 클래스를 만들어 전달하는 것이 편리하다.

또한 표현 영역에서 응용 서비스의 결과로 사용해야 하는 경우 응용 서비스에 에서의 결과로 필요한 데이터를 리턴한다.

ex) 상품 주문 후 주문상 세 내역을 조회하기 위해 주문번호를 리턴한다.

거기에 에그리거트를 리턴할 수도 있지만 필요한 값만 재정의 후 리턴하는 것이 좋으며 응집도를 높일수있다.

6.3.4 표현 영역에 의존하지 않기

응용 서비스의 파라미터 타입을 결정할 때는 표현 영역과 관련된 타입을 사용하면 안 된다.

그 예로 HttpServletRequest 나 HttpSession을 응용 서비스로 전달하면 안 된다.

이렇게 되면 단독으로 테스트하기가 어려워지며 표현 영역이 변경되면 응용 서비스 영역도 변경해야 할 수 있다.

더 큰 문제는 응용 서비스가 표현 영역을 대신할 수 있기 때문에 표현 영역의 응집도가 깨지게 돼 결과적으로 유지 보수 비용이 발생한다.

이러한 문제가 발생하지 않으려면 응용 서비스가 표현 영역의 기술은 사용하지 않도록 해야 한다.

6.3.5 트랜잭션 처리

트랜잭션은 프레임워크가 제공하는 트랜잭션 기능을 적극 사용하는 것이 좋다.

스프링에서 제공하는 @Transaction을 사용해 코드를 작성하면 트랜잭션 처리 코드를 간결하게 유지할 수 있다.

6.4 표현 영역

표현 영역의 역할 3가지

  1. 사용자에게 화면 제공 및 제어.

  2. 사용자의 요청에 맞는 응용 서비스 전달 및 결과 제공.

  3. 사용자의 세션 관리.

6.5 값 검증

값 검증 시 단일로 검증하지 않고 여러 개를 한 번에 검사하고 에러 목록에 추가하여 사용자에게 번거로움을 제공하지 않을 수 있다.

스프링에서는 Validator 인터페이스를 제공하기에 인터페이스를 사용한 검증기를 만들 수 있다.

Layer 별 검증의 범위

  • 표현 영역 - 필수 값, 값의 형식, 변위 검증.

  • 응용서비스 - 데이터의 유무 존재, 논리적 오류 검증.

하지만 이 책의 글쓴이는 요즘 응용 서비스에서 모든 걸 처리한다고 하며 이는 코드가 늘어가는 불편함이 있지만 응용 서비스의 완성도 높아지기 때문이라고 말한다.

6.6. 권한 검사

권한 검사는 보통 세 곳에서 이루어진다.

  1. 표현 영역

  2. 응용 서비스

  3. 도메인

표현 영역은 기본적인 인증된 사용자를 검사한다.

예로 회원정보를 변경할 때 이와 관련된 URL로 접근한 사용자가 인증된 사용자인지 확인한다.

만약 URL로 정자는 접근을 제어할 수 없는 경우 응용 서비스에서 메서드 단위로 권한 검사를 수행한다.

꼭 코드상 검사가 아니더라도 스프링 시큐리티 AOP 활용한 검중도 사용할 수 있다.

개별 도메인 객체 단위로의 검사의 경우 구현이 복잡해진다.

스프링 시큐리티 같은 보안 프레임워크를 사용해 프레임워크에 통합할 수도 있다.

하지만 보안 프레임워크를 확장하려면 프레임워크에 대한 높은 이해도가 필요하다.

그렇지 않다면 직접 구현하는 것이 코드 유지 보수에 유리하다.

6.7 조회 전용 기능과 응용 서비스

조회의 기능만 있는 서비스는 차라리 구현하지 않는 게 좋을 수도 있다.

바로 표현 영역에 구현해도 문제가 없다.

dd

REFERENCE

itssobeauti 도메인 주도 개발 시작하기