개발 성장기/DDD

[DDD START!] 6장. 응용 서비스와 표현 영역

주난v 2020. 5. 26. 08:11

도메인 영역만 잘 만든다고, 사용자의 요구를 충족한다고 끝나는 것은 아니다.

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

 

표현 영역 - 사용자의 요청 해석 (어떤 기능을 실행하고 싶어하는지...), 응용 서비스 실행
               - 응용 서비스에서 원하는 형태로 사용자의 요청을 변환

응용 영역 - 기능 제공

               - repository로 부터 도메인 객체를 구하고, 사용 

               - 유효성 검사, 애그리거트 CRUD, 결과 리턴 이외의 것이 들어있으면 도메인 로직의 일부를 구현하고 있을 가능성이 있다.

               - 트랜잭션 처리

 

도메인 로직 넣지 않기

public class ChangePasswordService {
	public void changePassword(String memberId, String oldP, String newP) {
    	Member member = memberRepository.findById(memberId);
        checkMember(member);
        member.changePassword(oldP, newP); // 도메인에서 실행
    }
}

도메인 로직을 응용 서비스와 도메인 영역에 분산해서 구현한다면 문제가 발생한다.

  1. 코드의 응집성이 떨어진다. (도메인 로직을 파악하기 위해 여러 영역을 분석해야 한다.)

  2. 여러 응용 서비스에서 동일한 도메인 로직을 구현할 수 있다.(코드 중복, 유지보수 어려움)

--> 도메인 로직을 도메인 영역에 분리하면, 코드 중복 문제는 발생하지 않고, 유지보수도 용이하다.

 

응용 서비스의 구현

응용서비스는 표현 영역과 도메인 영역의 매개체 - 파사드 디자인패턴과 같은 역할을 한다.

-  파사드 패턴 : 코드의 중복, 직접적인 의존 제거, 상위 개념의 인터페이스 제공 필요

 

 

응용 서비스의 크기

  • 한 응용 클래스에서 회원 도메인의 모든 기능 구현

    • 한 도메인과 관련된 로직이 한 클래스에 위치..코드 중복을 제거할 수 있다.

    • 반면 크기가 커진다. (연관성이 적은 코드가 뒤섞여있다.)

  • 기능별로 응용 클래스 분리 (범균님 선호 방식)

    • 한 응용 서비스에서 Max 3개의 기능을 구현한다.

    • 클래스의 개수는 많아지지만, 코드 품질을 일정 수준으로 유지하는데 도움을 준다.

    • 중복이 발생할 여지가 있는 코드는 별도 클래스에 로직을 구현

 

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

- 구현 클래스가 다수 존재하거나, 런타임에 구현 객체를 교체해야 할 경우 인터페이스를 유용하게 사용할 수 있다.(파일 소켓 읽기 등)

- 불필요하다.

 

메서드 파라미터와 값 리턴

- 응용서비스는 표현영역으로 부터 파라미터를 전달 받는 데, 2개 이상 존재하면 별도 데이터 클래스로 사용하는 것이 편리하다.

- 리턴 형태는 애그리거트를 그대로 리턴할 수도 있다.

   - 코딩은 편하지만, 도메인 로직 실행을 응용 서비스와 표현 영역 두 곳에서 할 수 있게 된다. 이는 코드를 분산시켜 응집도를 낮춘다.

 

표현 영역에 의존하지 않기

- 응용 서비스는 표현 영역의 변화에 따라서 변경되면 안되고, HttpServletRequest나 HttpSession과 같은 파라미터는 전달하면 안된다.

 

트랜잭션 처리

도메인 이벤트 처리 - 도메인에서 이벤트를 발생시키면 응용 서비스에서 처리

(ex. 변경 암호 이메일 발송 시, memberInitializePassword() -> passwordChangeEvent -> Events.handle())

이벤트를 사용하면 코드가 다소 복잡해지는 대신 도메인 간 의존성이나 외부 시스템에 대한 의존을 낮춰준다. (10장 11장에서 추후 자세히..)

 

표현 영역

  • 사용자가 사용하도록 흐름을 제공하고 제어한다.

  • 요청을 응용 서비스에 전달하고, 결과를 사용자에게 제공

  • 세션 관리

값 검증

- 표현 영역, 응용 서비스에서 모두 수행

- Spring MVC 인 경우 Form 은 Errors나 BindingResult 사용

- 단, 응용 서비스에서 값을 검사 시에 첫 번째 값에 대한 Exception을 발생 시키면, 나머지에 대해 검사를 하지 않는다.

   이는 사용자는 나머지 항목에 대한 피드백을 알 수 없으므로, 표현 영역에서 검사하는 것이 낫다. (Validator)

- 응용 서비스는 중복 여부와 같은 논리적 요류만 검사하면 된다.

  1. 표현 영역 : 필수 값, 값의 형식, 범위 검증

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

권한 검사

- 스프링 시큐리티나 아파치 Shiro 같은 프레임워크 사용

- 표현 / 응용 / 도메인에서 권한 검사 가능

- 보통 표현 영역에서 인증된 사용자여부를 체크 -> 서블릿 필터에서도 활용

- 스프링 시큐리티는 AOP를 활용해서 권한 검사 가능 (@PreAuthrozie("hasRole('ADMIN')"))

 

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

- 조회 전용 기능은 별도의 로직이 없다면, 굳이 서비스를 만들 필요 없이 표현 영역에서 조회해도 된다.

  -> 그러나, 우리 코드 컨벤션은 위배

 

응용 서비스가 존재해야 한다는 강박 관념을 가지면, 표현 영역에서 바로 repository를 접근 하는 것이 이상하지만, 굳이 불필요 하다면 만들지 않아도 된다.

- 개인적으론 코드 컨벤션이 있으면 맞춰주는 것이 좋다고 생각, 추후에 조회 후에 논리적 오류를 검사해야 한다거나, 값에 대한 변환이 필요할 경우 대비