Chapter 06. 응용 서비스와 표현 영역

beanii·2023년 3월 27일
0

DDD Study

목록 보기
6/11
post-thumbnail

6.1 표현 영역과 응용 영역

  • 정리

    • 1 ~ 5장 : 도메인의 구성요소와 JPA를 이용한 리포지터리 구현 방법
    • 도메인이 제 기능 하려면 사용자와 도메인 연결하는 매개체 역할을 하는 표현 영역과 응용 영역 필요
  • 표현 영역

    • 사용자 요청 해석
    • 요청을 받으면 URL, 요청 파라미터, 쿠키, 헤더 등 이용해서 사용자가 실행하고자 하는 기능 판별 및 기능 제공하는 응용 서비스 실행
    • 사용자로부터 전달받은 데이터가 응용 서비스가 요구하는 파라미터와 형식이 다르기 때문에 이를 변환해야 함

  • 응용 영역

    • 실제 사용자가 원하는 기능 위치함
    • 기능을 실행하느데 필요한 입력 값을 메서드 인자로 받고 실행 결과 리턴
    • 표현 영역에 의존하지 않음


6.2 응용 서비스의 역할

  • 도메인 서비스의 주요 역할
    • 도메인 객체를 사용해서 사용자의 요청 처리
      • 도메인 로직 구현하지 않도록 조심
    • 트랜잭션 처리
    • 접근 제어
    • 이벤트 처리

6.2.1 도메인 로직 넣지 않기

  • 도메인 로직을 도메인 영역과 응용 서비스에 분산해서 구현하면 생기는 문제

    1. 코드의 응집성 떨어짐
    • 도메인 로직을 파악하기 위해 여러 영역 분석해야 함
    1. 여러 응용 서비스에서 동일한 도메인 로직 구현할 가능성 높아짐(코드 중복)

    -> 결과적으로 고드 변경을 어렵게 만듦



6.3 응용 서비스의 구현

6.3.1 응용 서비스의 크기

  • 응용 서비스 구현 방법
    1. 한 응용 서비스 클래스에 회원 도메인의 모든 기능 구현하기
    • 장점 : 각 기능에서 동일 호직에 대한 코드 중복 제거 가능
    • 단점 : 한 클래스의 크기가 커짐 -> 코드 품질 낮추게 됨

    1. 구분되는 기능별로 응용 서비스 클래스를 따로 구현하기
    • 한 응용 서비스 클래스에 1 ~ 3개의 기능 구현
    • 장점 : 코드 품질 유지, 코드가 서로 의존적이지 않음
    • 각 기능마다 동일한 로직이 필요한 경우 별도 클래스에 로직 구현해서 코드 중복 방지

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

  • 응용 서비스를 구현할 때 인터페이스가 필요한가?
    -> 필요 없음, 전체 구조가 복잡해지므로 인터페이스가 명확하게 필요하기 전까지는 작성하지 않는 것이 좋음

  • 인터페이스 필요한 경우
    • 구현 클래스가 여러 개인 경우
    • 런타임에 구현 객체를 교체해야 하는 경우
    • -> 응용 서비스는 둘 다 해당 안 됨

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

  • 응용 서비스가 메서드를 실행하는 데 필요한 값을 파라미터로 받는 방법
    1. 필요한 각 값을 개별 파라미터로 전달받음
    2. 값 전달을 위해 별도 클래스를 만들어 전달받음
    • 스프링 MVC같은 웹 프레임워크는 웹 요청 파라미터를 자바 객체로 변환하는 기능 제공하므로, 요청 파라미터가 두 개 이상이면 별도 클래스 사용하는 것이 편리

  • 응용 서비스의 결과 리턴하기
    • 주문번호와 같이 필요한 데이터만 리턴 -> 기능 실행 로직 응집도 높임
    • 애그리거트 객체 그대로 리턴 -> 코드 응집도 낮춤

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

  • 응용 서비스가 표현 영역에 의존하면 생기는 문제점
    • 응용 서비스 단독으로 테스트하기 어려움
    • 표현 영역의 구현 변경되면 응용 서비스의 구현도 변경해야 함
    • 응용 서비스가 표현 영역의 역할까지 대신하는 상황 발생 -> 표현 영역의 응집도 꺠짐, 코드 유지 보수 비용 증가

서비스 메서드의 파라미터와 리턴 타입으로 표현 영역의 구현 기술 사용하지 않기


6.3.5 트랜잭션 처리

  • @Transactional
    • 스프링은 @Transactional이 적용된 메서드가 RuntimeException을 발생시키면 트랜잭션 롤백
    • 그렇지 않으면 커밋


6.4 표현 영역

[표현 영역의 책임]

  1. 사용자가 시스템을 사용할 수 있는 흐름(화면)을 제공하고 제어한다
    • 사용자가 요청한 내용을 응답으로 제공

  2. 사용자의 요청을 알맞은 응용 서비스에 전달하고 결과를 사용자에게 제공한다
    • 화면을 보여주는데 필요한 데이터를 읽거나 도메인의 상태를 변경해야 할 때 응용 서비스 사용
    • 아 과정에서 사용자의 요청 데이터를 응용 서비스가 요구하는 형식으로 변환하고, 응용 서비스의 결과를 사용자에게 응답할 수 있는 형식으로 변환
    • MVC 프레임워크는 HTTP 요청 파라미터로부터 자바 객체를 생성하는 기능 지원

  3. 사용자의 세션을 관리한다
    • 사용자의 연결 상태인 세션을 관리
    • 웹은 쿠키나 서버 세션 이용해서 사용자의 연결 상태 관리
    • 세션 관리는 권한 검사와 연결


6.5 값 검증

  • 값 검증은 표현 영역과 응용 서비스 두 곳에서 모두 수행 가능
  • 원칙적으로 모든 값에 대한 검증은 응용 서비스에서 처리 (값의 형식 검사, 로직 검사 등)
  • 표현 영역은 잘못된 값이 존재하면 이를 사용자에게 알리고 다시 값 입력받음 -> 코드가 다소 번잡

  • 응용 서비스에서 각 값이 유효하지 확인할 목적으로 익셉션 사용할 때의 문제점 : 사용자에게 좋지 않은 경험 제공
    ex) 전체 값에 대해 검사하지 않기 때문에 사용자는 같은 폼에 값을 여러 번 입력해야 함
    -> 응용 서비스에서 에러 코드를 모아 하나의 익셉션으로 발생시키는 방법 사용 (ValidationError, ValidationErrorException 사용)

  • 응용 서비스를 사용하는 표현 영역 코드가 한 곳이면 구현의 편리를 위해 표현 영역과 응용 서비스가 값 검사를 나눠서 수행 할 수도 있음
    • 표현 영역 : 필수 값, 값의 형식, 범위 등을 검증
    • 응용 서비스 : 데이터의 존재 유무와 같은 논리적 오류를 검증

  • 필자의 의견: 가능하면 응용 서비스에서 필수 값 검증과 논리적인 검증 모두 하는 편
    • 코드가 늘어날 수 있지만 응용 서비스의 완성도 높아짐


6.6 권한 검사

  • 개발하는 시스템마다 권한의 복잡도가 다르기 때문에 항상 고민해야 함
  • 다양한 상황을 충족시키기 위해 스프링 시큐리티와 같은 프레임워크는 유연하고 확장 가능한 구조 갖고 있음. 그러나 그만큼 복잡
  • 보안 프레인워크에 대한 이해ㅏ 부족하면 프레임워크를 무턱대고 도입하는 것보다 개발할 시스템에 맞는 권한 검사 기능 구현하는 것이 시스템 유지 보수에 유리

[권한 검사를 수행할 수 있는 곳]

  1. 표현 영역
    • 인증된 사용자인지 아닌지 검사
    • 서블릿 필터에서 사용자의 인증 정보 생성하고 인증 여부 검사
  2. 응용 서비스
    • URL만으로 접근 제어를 할 수 없는 경우 응용 서비스의 메서드 단위로 권한 검사 수행
    • 꼭 응용 서비스의 코드에서 직접 권한 검사 해야하는 것은 아님
      ex) 스프링 시큐리티 - @PreAuthorize("hasRole('ADMIN')
  3. 도메인
    • 개별 도메인 객체 단위로 권한 검사 해야 하는 경우는 구현 복잡
    • 보안 프레임워크 확장해서 사용
    • 프레임워크에 대한 이해가 부족하다면 직접 권한 검사 기능 구현하는 것이 코드 유지 보수에 유리


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

  • 5장에서 만든 조회 전용 모델과 DAO를 서비스에 사용하면 서비스 코드가 단순히 조회 전용 기능 호출하는 형태로 끝남
  • 서비스에서 추가적인 로직이 없고 단일 쿼리만 실행하는 기능이기 때문에 트랜잭션도 필요 없음

-> 굳이 서비스 만들지 않고 표현 영역에서 바로 조회 기능 사용해도 문제 없음

public class OrderController {
	private OrderViewDao orderViewDao;
    
    @RequestMapping("/myorders")
    public String list(ModelMap model) {
    	String ordererId = SecurityContext.getAuthentication().getId();
        List<OrderView> orders = orderViewDao.selectByOrderer(ordererId);
        medel.addAttribute("orders", orders);
        return "order/list";	
}
...

표현 영역에서 응용 서비스 없이 조회 전용 기능에 접근하는 것이 아상하게 느껴질 수 있지만 전혀 이상하지 않은 것!!

0개의 댓글

관련 채용 정보