Skip to content

Latest commit

 

History

History
62 lines (55 loc) · 6.31 KB

04_implemeating_a_use_case.md

File metadata and controls

62 lines (55 loc) · 6.31 KB

04. 유스케이스 구현하기

1. 도메인 모델 구현하기

2. 유스케이스 둘러보기

  • 일반적으로 유스케이스는 다음과 같은 단계를 따른다.
    1. 입력을 수신
    2. 비즈니스 규칙 검증 (입력 유효성 검증이 아니다!)
    3. 모델 상태 조작
    4. 출력을 반환
  • 유스케이스 코드는 도메인 로직에만 집중해야 하므로 입력 유효성 검증으로 오염되면 안된다고 말한다.
  • 하지만 유스케이스는 비즈니스 규칙을 검증할 책임이 있다!
  • 넓은 서비스 문제를 피하기 위해 각 유스케이스별로 분리된 각각의 서비스로 만들기를 권장한다.
  • 송금하기 유스케이스 코드

3. 입력 유효성 검증

  • 입력 유효성 검증은 유스케이스의 책임은 아니지만, 여전히 애플리케이션 계층의 책임에 해당한다고 말한다.
  • 왜냐하면, 입력 커맨드에 해당하는 SendMoneyCommand는 유스케이스를 통해 수신되므로 애플리케이션 계층에 속해 있어야 하기 때문이다.
  • 책에서는 생성자, Bean Validation 추상 클래스 등으로 SendMoneyCommand 생성시 유효성 검증을 하고 있다.
  • 이 부분이 조금 의문이었는데, Spring Validation을 이용하면 굳이 SendMoneyCommand에서 검증을 할 필요가 없기 때문이다.
  • 근데 이러면 애플리케이션 계층에서 웹 어댑터 계층이 Spring Validation을 사용한다는 의존성을 갖게 되기 때문에, 아마도 이 부분을 배제한게 아닌가 싶다.
  • 게다가 요청이 Request Body가 아닌, Path Variable 형식으로 올 수도 있어 책의 방식이 좀 더 나아보인다.
  • SendMoneyCommand

4. 유스케이스마다 다른 입력 모델

  • 각기 다른 유스케이스에 동일한 필드들이 존재하는 경우, 동일한 입력 모델을 사용하고 싶을 수 있다.
  • 그러나 이런 경우, 각기 다른 유스케이스 때문에 다음과 같은 상황이 벌어진다.
    • 어느 한 필드는 null이 되는 경우가 있다. 예를 들어, 보통 등록의 경우는 ID가 null이지만, 수정의 경우는 필수값이 된다.
    • 위의 경우, null을 유효한 상태로 받아들이는 code smell이 발생한다.
    • 반대로, 등록의 경우에 ID값이 들어오면 어떻게 해야할까? 그냥 진행? 에러 발생?
    • 게다가 추후 수정에만 필요한 필드들이 추가된다면 어떻게 하겠는가?
  • 이와 같은 상황들로 인해, 각 유스케이스 전용 입력 모델 사용은 유스케이스를 훨씬 더 명확하게 만든다.
  • 그리고 다른 유스케이스와의 결합도 제거하여 side-effect를 발생시키지 않는다.

5. 비즈니스 규칙 검증하기

  • 비즈니스 규칙 검증은 분명한 유스케이스 로직의 일부다.
  • 그렇다면, 입력 유효성 검증과 비즈니스 규칙 검증은 어떻게 구분될까?
  • 이는 도메인 모델의 상태의 접근 여부로 알 수 있다.
    • 비즈니스 규칙 검증은 현재 도메인 모델의 상태에 접근해야 한다.
    • 예를 들어, "출금 계좌는 초과 출금되어서는 안된다." 라는 규칙은 현재 출금 계좌의 상태에 접근해야 알 수 있는 비즈니스 규칙이다.
    • 반면에, "송금되는 금액은 0보다 커야한다." 라는 규칙은 도메인 모델에 접근하지 않고도 구현할 수 있는 입력 유효성 검증이다.
  • 그렇다면, 비즈니스 규칙 검증은 어디에 구현하면 좋을까?
  • 가장 좋은 방법은 도메인 엔티티 안에 넣는 것이다.
  • 이렇게 하면 이 규칙을 지켜야 하는 비즈니스 로직 바로 근처에 규칙이 위치하기 때문에 빠르게 그 검증내용을 확인할 수 있다.
  • 만약, 도메인 엔티티에서 비즈니스 규칙을 검증하기 여의치 않다면(빈약한 도메인 모델인 경우 등), 유스케이스 코드에서 도메인 엔티티를 사용하기 전에 해도 된다.

6. 유스케이스마다 다른 출력 모델

  • 입력 모델의 예와 동일하게, 출력 모델도 유스케이스마다 다르게 유지하는 것이 좋다.
  • 반환하는 데이터는 가능한 한 적게 유지하자.
  • 출력 모델을 다르게 유지하면, 다른 유스케이스와의 결합을 제거하는데 도움이 된다.
  • 같은 이유로, 도메인 엔티티를 출력 모델로 사용하면 안된다. 이는 도메인 엔티티에 변경할 이유가 필요 이상으로 늘어나는 상황을 초래한다.

7. 읽기 전용 유스케이스(조회 유스케이스)는 어떨까?

  • 애플리케이션 코어 관점에서 이 작업은 간단한 데이터 쿼리이다. 그렇기 때문에, 프로젝트 컨텍스트에서 유스케이스로 간주되지 않는다면, 실제 유스케이스와 구분할 필요가 있다.
  • 쿼리를 위한 인커밍 전용 포트를 만들고, 이를 쿼리 서비스에 구현하는 방법이 있다.
  • GetAccountBalanceService
  • 이처럼 읽기 전용 쿼리는 쓰기가 가능한 유스케이스(또는 커맨드)와 코드 상에서 명확하게 구분된다. 이런 방식은 CQRS(Command-Query Responsibility Segregation) 같은 개념과 아주 잘 맞는다.

8. 정리

  • 유스케이스별 서비스 생성, 유스케이스별 입출력 모델 생성, 모델간 매핑 코드 증가 등 파일 갯수가 증가하는 복잡도는 올라갈 것으로 보인다. 이 점은 의외로 많은 개발자들의 반발을 살지도 모른다.
  • 그러나 이는 관심사를 철저히 분리하여 최대한 독립적인 개발이 가능하게 하여 비즈니스 로직 구현의 복잡도는 크게 낮출 것으로 예상된다.
  • 충분히 적용해 볼만한 내용으로 생각된다.