//
Search
Duplicate
📖

04 [만들면서 배우는 클린 아키텍처] 유스케이스 구현하기

저자
톰 홈버그
점수
⭐️⭐️⭐️
완료일
2023/02/09
상태
완료
유형

목차

도메인 모델 구현하기.

code
Account 엔티티는 실제 계좌의 현재 스냅을 제공한다. Account에 대한 모든 입금과 출금은 Activity 엔티티에 포착된다. 한 계좌에대한 모든 활동들을 항상 메모리에 올리는것은 현명한 방법이아니다. Account 엔티티는 ActivityWindow값 객체에서 포착한 지난 며칠 혹은 몇 주간의 활동만 보관한다.
계좌를 계산하기 위해서 Account 엔티티는 활동창 (ActivityWindow)의 첫번째 활동 바로 전의 잔고를 표현하는 baselineBalance속성을 가지고 있다.때문에 총 잔고는 기준잔고 + 활동창의 모든 잔고를 더한 값이 된다.
이 모델 덕분에 입금과 출금은 withdraw()와 deposit()메소드에 추가하는 것에 불과하다.
이제 입금과 출금을 할 수 있는 Account 엔티티가 있으므로 이를 중심으로 유스케이스를 구현하기 위해 바깥 방향으로 나아갈 수 있다.

유스케이스 둘러보기

일반적을 유스케이스는 다음과 같은 단계를 따른다.
1.
입력력을 받는다.
2.
비지니스 규칙을 검증한다.
3.
모델 상태를 조작한다.
4.
출력을 반환한다.
유스케이스는 인커밍 어댑터로부터 입력을 받는다.
유스케이스는 비지니스 규칙 (bussiness rule)을 검증할 책임이 있다.
비지니스 규칙을 충족하면 유스케이스는 입력을 기반으로 어떤 방법으로든 모델의 상태를 변경한다. 일반적으로 도메인 객체의 상태를 바꾸고 영속성 어댑터를 통해 구현된 포트로 이 상태를 전달해서 저장될 수 있게한다.
‘송금하기’ 유스케이스를 구현하는 방법을 보자.
넓은 서비스 문제를 피하기 위해서 모든 유스케이스를 한 서비스 클래스에 모두 넣지 않고 각 유스케이스별로 분리된 각각의 서비스로 만들겠다.
code
1.
서비스는 인커밍 포트 인터페이슨 SendMoneyUseCase를 구현
2.
계좌를 불러오기위해 아웃고잉 포트 인터페이스 LoadAccountPort 호출
3.
데이터 베이스 계좌상태를 변경하기 위해 UpdateAccountStatePort를 호출
그림 하나의 서비스가 하나의 유스케이스를 구현하고, 도메인 모델을 변경하고, 변경된 상태를 저장하기 위해 아웃고잉 포트를 호출 한다.

입력 유효성 검증

호출하는 어댑터가 유스케이스에 입력을 전달하기 전에 입력 유효성을 검증하면 어떨까? 과연 유스케이스에서 필요로 하는 것을 호출자가 모두 검증했다고 받을 수 있을까?
에플리케이션 계층에서 입력 유효성을 검증하는 이유는, 그렇게 하지 않을 경우 코어의 바깥쪽으로부터 유효하지 않은 입력값을 받게되고, 모든상태를 해칠수 있기 때문이다.
입력모델(input model)이 이문제를 다루도록 해보자. ‘송금하기’ 유스크에스에서 입력모델은 SendMoneyCommand 클래스이다. SendMoneyCommand의 필드에 final을 지정해 불변 필드로 만들었다.
SendMoneyCommand은 유스케이스 API의 일부이기 때문에 인커밍 포트 패키지에 위치한다. 자바 세계에는 Bean Validation API가 이러한 작업을 위한 사실상의 표준 라이브 러리이다. 필요한 유효성 규칙들을 필드와 애너테이션으로 표현 할 수있다.
code
입력 모델에 있는 유효성 검증 코드를 통해 유스케이스 구현체 주위에 사실상 오류 방직계층을 만들었다.

생성자의 힘

예제 코드의 생성자에는 3개의 파라미터만 있다. 파라미터가 더 많다면 어떻게 해야 할까? 빌더(Builder) 패턴을 활용하면 더 편하게 사용할 수 있지 않을까? 긴 파라미터 리스트를 받아야 하는 생성자를 private으로 만드록 빌더의 build() 메서드 내부에 생성자호출을 숨길 수 있다. 하지만 이는 유효하지 않은 상태의 불변 객체를 만들려는 시도에 대해서 경고해주지 못한다.
생성자를 직접 사용했다면 새로운 필드를 추가하거나 필드를 삭제할 때마다 컴파일 에러를 따라 나머지 코드에 변경사항을 반영할 수 있을 것이다.
파라미터들을 헷갈리지 않도록 IDE가 파라미터명 힌트를 보여준다.

비지니스 규칙 검증하기

비지니스 규칙은 에플리케이션의 핵심이기에 적절하게 잘 다뤄야 한다. 그런데 언제 입력 유효성을 검증하고 언제 비지니스 규칙을 검증해야 할까?
둘 사이의 아주 실용적인 구분점은 비지니스 규칙을 검증하는 것은 도메인 모델의 현재 상태에 접근해야 하는 반면, 입력 유효성 검증은그럴 필요가 없다는 것이다.
입력 유효성을 검증하는 것은 구문상의(syntactical) 유효성을 검증하는 것
비지니스 규칙은은 유스케이스의 맥락 속에 의미적인(semantical) 유효성을 검사하는것
예시)
“출금 계좌는 초과 출금되어서는 안된다.” 정의에 따르면 이 규칙은 출금 계좌와 입금계좌가 존재하는지 확인하기 위해 모델의 현재 상태를 접근해야 된다. → 비니지니스 규칙
“송금되는 금액은 0보다 커야 한다” 모델에 접근하지 않는다 → 유효성 검증
그러면 비지니스 규칙 검증은 어떻게 구현할까?
가장 좋은 방법은 앞에서 “출금 계좌는 초과 인출되어서는 안 된다.” 규칙에서처럼 비즈니스 규칙을 도메인 엔티티 안에 넣는 것이다.
code
만약 도메인 엔티티에서 비지니스 규칙을 검증하기가 여의치 않다면 유스케이스 코드에서 도메인 엔티티를 사용하기 전에 해도 된다.
code
유효성을 검증하는 코드를 호출하고, 유효성 거증이 실패할 경우 요호성 검증 전용 예외를 던진다.
도메인 모델을 로드해야 한다면 앞에서 “출금 계좌는 초과 출금되어서는 안된다.” 규칙을 다뤘을 때처럼 도메인 엔티티 내에 비지니스 규칙을 구현해야 한다.

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

입력과 비슷하게 출려도 가능하면 각 유스케이스에 맞게 구체적을시록 좋다. 출력은 호출자에게 꼭 필요한 데이터만 들고 있어야 한다. 유스케이스를 가능한 구체적으로 유지하기 위해서는 계속 질문해야 한다. 만약 의심스럽다면 가능 한 적게 반환하자.
유스케이스들 간에 같은 출력 모델을 공유하게 되면 유스케이스들도 강하게 결합된다. 한 유스케이스에서 출력 모델에 새로운 필드가 필요해지면 이 값과 관련이 ㅇ벗는 다른 유스케이스에서도 이 필드를 처리해야한다. 때문에 단일 책임 원칙을 적용하고 모델을 분리해서 유지하는 것은 유스케이스의 결합을 제거하는데 도움이 된다.

읽기 전용 유스케이스는 어떨까?

UI에서 계좌의 잔액을 표시해야 한다고 가정해보자. 이를 위한 새로운 유스케이스를 구현해야 할까?
프로젝트 맥락에서 유스케이스로 간주되지 않는다면 실제 유스케이스와 구분하기 위해 쿼리로 구현할 수 있다.
이책의 아키텍처 스타엘에서 이를 구현하는 한가지 방법은 쿼리를 위한 인커밍 전용 포트를 만들고 이를 ‘쿼리 서비스’에 구현하는 것이다.
Code
서비스는 아웃고잉 포트로 쿼리를 전달하는 것 외에 다른 일을 하지 않는다. 여러 계층에 걸쳐 같은 모델을 사용한다면 지름길을 서서클라이언트가 아웃고잉 포트를 직접 호출하게 할 수 도 있다. 이부분은 11장에서 자세하게 다룬다.

유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?

이 책의 아키텍처에서는 도메인 로직을 우리가 원하는 대로 구현할 수 있도록 허옹하지만, 입출력 모델을 독립적으로 모델링한다면 원치 않는 부수효과를 피할 수 있다. 물론 유스케이스 간에 모델을 공유하는 것보다는 더 많은 작업이 필요하다. 각 유스케이스 별도의 모델을 만들어야하고, 이모델과 엔티티를 매핑해야 한다.
그러나 유스케이스별로 모델을 만들면 유스케이스를 명확하게 이해할 수 있고, 장기적으로 유지보수하기도 더 쉽다.
꼼꼼한 입력 유효성 검증, 유스케이스별 입출력 모델은 지속 가능한 코드를 만드는 데 큰 도움이 된다.

리뷰

오늘 4장 유스케이스에 대해 읽게되었다. 유스케이스와 도메인 작성법에 대해 배울 수 있었고, 특히 각 유스케이스마다 별도의 모델을 만들어야 하고, 맵핑해야 된다는 대목이 눈에 띄었다. 좀 더 많은 클래스들을 만들어야 되지만, 하나의 유스크에스를 명확하게 이해하는데 좋고, 다른 사람들도 보기 편하다는 것이다. 책을 다읽고 한번 위와 같은 형식의 코드를 짜보는 것도 해봐야 겠다.
책 소개 : 만들면서 배우는 클린 아키텍처