코딩은 마라톤

[도메인 주도 개발 시작하기] Chapter6. 응용서비스와 표현 영역 본문

Backend

[도메인 주도 개발 시작하기] Chapter6. 응용서비스와 표현 영역

anxi 2024. 11. 18. 02:00

표현 영역과 응용 영역

  • 표현 영역 : 사용자의 요청 해석, 요청에 상응하는 응용 서비스 실행
  • 응용 영역 : 실제 사용자가 원하는 기능을 제공하는 서비스

응용 서비스 역할

  • 응용 서비스는 사용자의 요청을 처리하기 위해 리포지터리에서 도메인 객체를 가져와 사용한다.
  • 응용 서비스는 도메인 영역과 표현 영역을 연결하는 창구
  • 응용 서비스가 복잡하면 서비스에서 도메인 로직 일부를 구현하고 있을 수 있음 → 도메인 로직을 서비스에서 사용하고 있을 수 있음
  • 응용 서비스는 트랜잭션 처리 담당

도메인 로직 넣지 않기

도메인 로직을 응용 서비스에 분산해서 구현할 경우 문제점

  • 코드 응집성 떨어짐
    • 도메인 데이터와 도메인 로직이 한 영역에 위치하지 않기 때문
  • 여러 응용 서비스에서 동일한 도메인 로직을 구현할 가능성이 높아짐

응용 서비스 구현

응용 서비스는 표현 영역과 도메인 영역을 연결하는 매개체 역할 → 파샤드 (디자인 패턴)와 유사

응용 서비스 크기

  • 한 응용 서비스 클래스에 모든 기능 구현하기 (도메인 별)
    • 장점 : 한 도메인과 관련된 기능을 구현한 코드가 한 클래스에 위치하므로 동일 로직에 대한 코드 중복 제거
    • 단점 : 서비스 클래스 크기가 커진다
public class MemberService { ... }
  • 구분되는 기능별로 응용 서비스 클래스 따로 구현하기
    • 단점 : 클래스 개수 많아짐
    • 장점
      • 코드 품질을 일정 수준으로 유지하는데 도움
      • 각 클래스별로 필요한 의존 객체만 포함
public class ChangePasswordService { ... }

만약 각 응용 서비스에서 공통되는 로직이 있다면 별도 클래스로 구현한다.

public final class MemberServiceHelper { ... }

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

public interface MemberService {
  public void changePassword(...);
}

public class MemberServiceImpl implements MemberService { 
  ... 구현
}

위와 같이 인터페이스를 만들고 구현체에서 구현하는 경우를 많이 봤을 것이다.

인터페이스가 필요한 상황

  1. 구현 클래스가 여러 개일 때 사용

하지만 응용 서비스의 인터페이스가 두 개 이상의 클래스로 구현될 경우는 거의 없다.


표현 영역

표현 영역의 책임

  • 사용자가 시스템을 사용할 수 있는 흐름(화면)을 제공하고 제어한다.
  • 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공한다.
  • 사용자의 세션을 관리한다.

값 검증

값 검증은 표현 영역과 응용 서비스에서 수행될 수 있다.

표현 영역에서 값을 검증할 경우, 잘못된 값이 존재하면 사용자에게 알려주고 값을 다시 입력받는다.

응용 서비스에서 각 값이 유효한지 확인할 목적으로 익셉션을 발생할 때, 처음 익셉션이 발생하면 이후 값 검증은 하지 않고 첫 익셉션의 에러만 사용자가 알 수 있다.

이러한 사용자의 불편을 해소하기 위해 여러 에러 코드를 모아 하나의 익셉션으로 발생시킬 수 있다.

@Transactional
public OrderNo placeOrder(OrderRequest orderRequest) {
  List<ValidationError> errors = new ArrayList<>();
  if (orderRequest == null) {
    errors.add(ValidationError.of("empty");
  } else {
    // 조건 1
       errors.add(...)
    // 조건 2
       errors.add(...)
    ...
  }
  if (!errors.isEmpty()) throw new ValidationErrorException(errors);
  ...
} 

스프링 같은 프레임워크는 값 검증을 위한 Validator 인터페이스를 사용한다.

  • 표현 영역 : 필수 값, 값의 형식, 범위 등을 검증한다.
  • 응용 서비스 : 데이터의 존재 유무와 같은 논리적 오류를 검증한다.

→ 하지만 요즘은 응용 서비스에서 필수 값 검증과 논리적인 검증을 모두 하는 편이다. (응용 서비스의 완성도가 높아지는 이점)


권한 검사

권한 검사는 크게 세 곳에서 할 수 있다.

  • 표현 영역
    • 인증된 사용자인지 검사
    • 예시) 회원 정보 변경 기능
    • 서블릿 필터에서 인증 정보를 생성하고 인증 여부를 검사한다.

 

  • 응용 서비스
    • 메서드 단위로 권한 검사 수행
    • ex) Spring Security - AOP 사용
    • @PreAuthorize(”hasRole(’ADMIN’)”)
  • 도메인

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

서비스에서 조회 전용 기능을 사용하면 서비스 코드는 다음과 같다.

public class OrderListService {
  public List<OrderView> getOrderList(String ordererId) {
    return orderViewDao.selectByOrderer(ordererId);
  }
...

조회 기능만 존재하기 때문에 추가적인 로직은 존재하지 않는다.

단일 쿼리만 실행하는 조회 전용 기능이기 때문에 트랜잭션 또한 필요하지 않기 때문에 서비스에 로직을 넣지 않고 위 코드를 표현 영역에서 바로 사용한다.