View Request Model & Request Model

2017-11-13

어플리케이션을 도메인 모델을 기반으로 만들었다면 도메인 모델이 핵심일겁니다. 그런데 지금 이야기 하고자 하는것은 도메인 모델이 아니죠

도메인 모델은 비지니스의 정책을 담고 있는 모듈입니다. (Entity & Value Object & Service or Interator)

웹이든 배치이든 메시깅 기반이든 어플리케이션의 종류와 상관없이 독립적으로 만들어지고 유지되는게 제일 좋을 겁니다.

이런 도메인 모델에서 유스 케이스를 담당하고 있는 Interactor에 대해 유스 케이스를 실행시키기 위해 필요한 모든 파라메터를 담고 있는 Requet Model이 필요합니다.

Request Model은 유스케이스에 필요한 엔티티 관점의 요청 값들입니다. 이 Request Model도 도메인 모델의 일부이기 때문에 어플의 종류와 상관없이 재활용 가능해야 합니다. (가급적)

그런데 이런 Request model은 어플의 환경와 일치할때도 있지만 대부분의 경우 일치할수가 없습니다.

예를 들어 Web의 Form에서 생일을 입력받을때 html form으로는 year, month, day 3개의 input 을 받게 될 겁니다.

하지만 Request 모델에서는 java.util.Date를 받겠지요.

또한 검증 규칙도 다를겁니다. html form에서는 기본적인 검증이라면 year, month, day가 숫자일것을 검증하겠지만, request model에서는 오늘 현재보다 미래를 생일로 가지면 않됨.. 이렇게 비지니스적으로 합당한가를 검증할것입니다.

또한 동일한 유스케이스를 사용하지만 View 가 달라서 다른 Html form을 가지는 경우도 있을겁니다.

이렇게 서로 다른 관심사를 가지는게 일반적이기 때문에 View Request Model이 필요합니다.

View Request Model은 웹, 배치등 어플리케이션마다 정의되겠죠.

만약 어플리케이션이 Web Rest 어플리케이션이고 PATCH에서 null인 값과 삭제된 값이 각각 구분 되어야 한다면 View Request Model에 이를 반영해야 할겁니다 clearedFields 같은 추가적인 field를 이용해 값이 clear 된 것들을 알수 있게 해주어야 할겁니다. 이러한 View Request Model은 요청 핸들러 마다 만들어질수도 있을 겁니다. Lombok등을 이용하면 좀더 쉬워 질겁니다.

물론 이렇게 레이어를 나누면 너무 많은 객체가 만들어지게 되고 이런게 불편한 분들도 많습니다.

그래서 Request Model을 View Request Model로 사용하는 프로젝트도 많습니다.

이런 프로젝트는 얼마 지나지 않아 View 의 관심사항이 도메인 모델을 침습하게 됩니다.

웹에서 String으로 받으니까 숫자나, Value Object로 처리해야 하는 것을 그냥 String으로 선언해버리는 사람들이 나오게 됩니다 Request Model에 기술해둔 Validation Rule은 공통화 되지 못하기 때문에 의미를 상실하게 됩니다

프로젝트가 진행되다 보면 결국 Map과 그다지 차이 나지 않는 요청용 Dto가 나오게 되는거죠.

결국 Map으로 돌아 서던가, 아니면 이름 정도는 validation할수 있는 그다지 의미있지 않는 Dto를 양산하던가.. 그렇게 되는 경우가 많습니다.

여기에서 어떤 trade-off 를 할것인가는 프로젝트 구성원의 성향에 따르게 되겠습니다만, 저는 견고한 레이어링을 하는것을 추천합니다..

@Controller
class AController {
  @Data
  public static class ChangeBirthdayForm {
    @NotNull @Min(1917) Integer year;
    @NotNull @Min(1) @Max(12) Integer month;
    @NotNull @Min(1) @Max(31) Integer day;
  }

  public ResponseEntity changeBirthday(@Valid @ModelAttribute ChangeBirthdayForm form) {
    Date newBirthDay = new Date(year -1900, month -1, day);
    UserBirthdayChangeRequest request = new UserBirthdayChangeRequest(newBirthDay);
    userInfoInteractor.changeBirthday(request);
  }
}

@Data @AllArgsConstructor
class UserBirthdayChangeRequest {
  Date birthday;
  public void validate() {
    assertNotNull (birthday);
    assert(DateUtil.pastThan(newDate, now));
  }
}

@Service
class UserInfoInteractor {
  public void changeBirthday(@Valid UserBirthdayChangeRequest request){
    assert(userGradePolicy.acceptUserAge(request.getBirthday()));
  }
}
Tags: view  model  dto  request