오늘은 코드를 작성하는 것보다, 책을보고 공부하고, 기존의 프로젝트를 어떻게 개선 및 개발해야할지에 대한 설계를 했다.
신발 주문 시스템의 아키텍처 구조는 애플리케이션과 웹, 영속성 계층이 느슨하게 결합되어 있어, 도메인 모델링을 자유롭게 할 수 있다.
그럼 도메인 모델링은 어떻게 이뤄질까? 나는 도메인 엔터티를 시작으로 유스케이스(도메인 서비스, 애플리케이션서비스)를 개발했다.
나는 설계 단계에서 도메인 코어와 애플리케이션을 분리하기로 결정하였다.
당시 나는 비즈니스로직과 도메인 로직을 다르다고 판단하고, 다르게 사용하였다. 비즈니스로직은 유스케이스를 의미하고, 도메인 로직은 도메인 객체가 생성될 수 있는 조건, 각 프로퍼티의 검증을 의미했다.
해당 단어의 사용은 다른 개발자에게 혼란을 주었던 것 같다. 가장 많이 들었던 이야기는 도메인 로직과 비즈니스로직의 차이점이 무엇이냐? 라는 질문을 정말 많이 들었다. 나는 내 생각을 이야기했다. Money 라는 객체가 있을 때, Money 객체를 활용하는 것이 비즈니스로직, Money 객체 내의 price 는 -(음수)가 될 수 없다는 것(Money의 규칙)이 도메인 로직이라고 설명을했다. 그런데 이번 유스케이스를 공부하면서 도메인/비즈니스 로직에 대해 생각을 많이 하게 되었다.
유스케이스를 공부하며
입력을 받고, 비즈니스 규칙을 검증하고, 모델 상태를 조작하고, 출력을 반환한다.
유스케이스는 input 어댑터(ApplicationService)로부터 입력을 받는다. 그리고 세부 비즈니스 규칙을 검증하고, 모델 상태를 조작하고, 출력을 반환한다. 나는 이런 구조로 Member 도메인을 개발했다. 그리고 지금, 나는 이 Member 도메인에 문제가 있음을 알게되었다.
그것은 입력 유효성 검사가 MemberApplicationService에서 진행되었다.
왜 ApplicationService 에서 유효성을 검사하는 것이 문제가 될까? 첫번째로, 값이 유효하지 않은 상태에서 객체가 생성된 후, ApplicationService로 전해져서야 값이 유효하지 않음을 알 수 있다. 즉, 불필요한 객체가 생성된다. 이는 명백한 낭비이다. 두번째로, ApplicationService의 책임에 관해서 이야기 할 수 있다.
애플리케이션 계층(ApplicationService)의 책임
나는 해당 객체가 최초로 사용되는 곳인 애플리케이션 계층에서 값을 검증하고자 하였다. 하지만 책을 일고선 애플리케이션 계층의 책임에 입력 유효성은 어울리지 않는다는 생각이 들었다.
이유1. 애플리케이션 계층에서는 유스케이스가 구현되어있다. 즉, 입력 유효성 검사가 아닌, 비즈니스 규칙(business rule)을 검증해야한다. 아니 비즈니스 규칙 검사하면서 입력 유효성 검사도 할 수 있는 것 아니야? 할 수 있지만 이는 도메인 로직(유스케이스)를 더럽히는 행동이라 생각했다. (도메인을 왕으로 생각하는 프로젝트인데 앞뒤가 맞지 않는 행동이라는 생각이다.)
이유2. 애플리케이션계층을 통해 접근하는 도메인 코어에서 도메인로직을 통한 검증이 이뤄진다. 사실 이는 도메인 부분으로 다르게 생각이 될 수 있지만, 만약 애플리케이션계층(ApplicationService)에서 입력 유효성검사를 진행한다면, 로직의 반복이자 불필요한 코드의 추가라는 생각이 들었다.
입력 모델(Input Model) 와 데이터 전송객체(Data Transfer Object)
그럼 어디서 하는것이 좋을까? 에 대한 고민이 계속되었고, 책에서는 Input Model 을 이야기했다. Input Model은 들어오는 데이터를 캡슐화하고 애플리케이션의 사용 사례에 필요한 입력 매개변수에 대한 표준화된 표현을 제공하는 경계 객체 역할을 담당한다. 그리고 이는 외부 구성 요소에서 사용하는 특정 데이터 형식이나 프로토콜을 내부 애플리케이션 로직에서 분리하는데 도움이된다.
이러한 Input Model은 내가 사용하는 DTO을 의미했다. 나는 DTO를 통해 외부 요청또는 요청 데이터를(웹계층)에서 내부 레이어(애플리에이션계층)으로 전달했다. 이는 계층 간 데이터 전송 책임 측면에서 바라볼 때, Input Model과 DTO는이와 동일하다 생각한다.
그렇다면 DTO 객체에서 입력 유효성 검사를 진행하는 것이 맞을까? 아주 적절하다. 딱 DTO의 책임에 해당하는 부분이라는 생각이 들었다. 동시에 아 내가 왜 DTO를 생성할 때, 입력값 검증을 하지 않았을까? 하는 생각이 들었다.
최초로 사용되는 곳에서 유효성을 검사해야할까? 아니다. 생성될 때 입력값에 대한 검증이 이뤄져야한다. 초기에는 입력값에 대한 조건을 알기 위해서는 비즈니스 규칙을 알아야하는 것이 아닐까? 했었다. 비즈니스 규칙이 바뀌면, DTO에 직접적인 영향이 생기는가?를 고민해보니 아니였다. DTO는 변경되지 않고, 비즈니스 규칙만 변경되었을 때, 예외가 발생한다면? DTOException 이 아닌 DomainException이 발생할 것이다. 따라서 나는 현 개발중인 Product 를 개발할 때, 이 부분또한 적용하고, 비교해보고자 한다.
추가적으로 비즈니스 규칙은 어디에서 검증해야할까? 당연하게도(?) 도메인 엔터티 혹은 유스케이스를 다루는 ApplicationService(내 경우 CommandHandler)에서 검증하면 될 것이다.
마지막으로 책에서 본 부분 중 조금 헷갈리는 부분이 있다. 복잡한 비즈니스 규칙의 경우 먼저 데이터베이스에서 도메인 모델을 로드해서 상태를 검증해야 할 수도 있다고 하는데, 이에 대한 예시가 어떤 것이 있을까? 도메인 모델을 로드해야 한다면 도메인 엔터티 내에 비즈니스 규칙을 구현해야 한다고 한다는데, 머릿 속 N 의 회로가 무진장 돌아가는데 잘 상상이 안된다..
요즘에는 계층을 나누고, 계층별 클래스를 작성하면서, 각 계층과 클래스의 책임에 대한 생각이 많아지는 것 같다. 지금껏 나는 특정 기능을 위해 특정 클래스는 존재한다. 라고 생각했는데, 항상 기능이 책임이 되지는 않았다. 그래서 요즘에는 앞으로의 개발과 이전의 개발에 대한 생각이 더 많아지는 것 같다. 재밌다.
'회고 > TIL' 카테고리의 다른 글
ApplicationService(유스케이스) 구현과 고민 (7) | 2023.06.30 |
---|---|
Product 도메인 계층 개발, 입력 유효성 검사, 테스트 (0) | 2023.06.29 |
Product 테스트 작성, 좋은 테스트에 관하여, Member 피드백 (0) | 2023.06.27 |
Product 도메인 설계 및 개발 시작, 만들면서 배우는 클린아키텍처 (0) | 2023.06.26 |
6월 마지막 주 주말, 클린아키텍처 공부 (0) | 2023.06.25 |