만들면서 배우는 클린아키텍처를 읽고(1/4) (tistory.com)
만들면서 배우는 클린아키텍처를 읽고 (2/4) (tistory.com)
4장 유스케이스 구현하기
도메인 모델 구현하기
- Account 도메인의 컨셉
- ActivityWindow에 Activity 목록을 가지고있으며, Activity 목록을 계산하여 balance를 계산
- 최적화를 위해 특정 기간내의 Activity만 불러오며, 그 이전의 값은 baselineBalance로 미리 계산
유스케이스 둘러보기
- 유스케이스는 도메인 로직만 있어야함.
- 즉, 입력 유효성 검증은 이미 되었다고 가정
- 그러나, 비즈니스 규칙의 검증은 해야됨
입력 유효성 검증
- 유스케이스의 입력 모델을 이용해서 입력 유효성 검증을 진행
- Bean Validation API를 이용하면, 생성자가 아니라 annotation을 이용해서 검증 가능함
생성자의 힘
- 생성자 파라미터가 많아지면 Builder 패턴을 쓰는것을 권장하지만 저자는 chaining할 때 빼먹을 수 있으니, 강제력 측면에서 생성자 사용을 추천함
유스케이스마다 다른 모델
- 유스케이스마다 비슷한 데이터여도 다른 입력모델을 사용하는 것을 권장
- 비슷해보이지만, 서로 다른 유효성 검증 규칙을 가지기 때문에 code smell이 될 가능성이 높음
비즈니스 규칙 검증하기
- 입력 유효성 검증은 syntactical 측면이고 비즈니스 규칙은 semantical 측면을 검증하는 것
- 비즈니스 검증 규칙의 위치
- 도메인 엔티티 내 비즈니스 규칙에 위치 (비즈니스로직과 검징이 같이 있어 가독성이 좋음)
- 유스케이스 코드에서 도메인 엔티티 사용 전에 사용
풍부한 도메인 모델 vs. 빈약한 도메인 모델
- 풍부한 도메인 모델
- 엔티티에서 많은 로직을 구현하고 유스케이스는 엔티티의 진입점으로 활용
- 빈약한 도메인 모델
- 도메인은 Getter, Setter 만 활용하고 유스케이스에서 비즈니스 로직을 구현
유스케이스마다 다른 출력 모델
- 호출자에게 꼭 필요한 데이터만 들고 있어야한다.
- 잘 모르겠다면, 가능한 적게 반환하자
- 같은 출력 모델을 사용하게되면, 강하게 결합되는거기 때문에 조심해서 사용해야된다. (단일책임원칙)
읽기 전용 유스케이스는 어떨까?
- 유스케이스로 분류하지 않고 Query 서비스로 구분하여 읽기 전용 서비스 제공
- CQRS 패턴과 일맥상통
유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?
- 입출력모델을 독립적으로 모델링 한다면, 원치 않는 부수효과를 피할 수 있다.
==> 객체지향적인게 아니고 절차지향적이게 되는건 아닌가?
5장 웹 어댑터 구현하기
의존성 역전
- 웹 어댑터와 유스케이스 사이에 포트를 둬서 의존성 역전을 적용함
- 포트는 Application Core와 외부 세계와 통신하기위한 명세
- 포트의 적절한 배치를 통해 외부와 어떤 통신을 하는지 명확히 구분 가능
웹 어댑터의 책임
- 웹 어댑터가 하는 일
- HTTP 요청을 자바 객체로 매핑
- 권한 검사
- 입력 유효성 검증
- 입력을 유스케이스의 입력 모델로 매핑
- 유스케이스 호출
- 유스케이스의 출력을 HTTP로 매핑
- HTTP 응답을 반환
- 어플리케이션은 웹 어댑터가 HTTP 관련 처리를 하는지 상관없이 독립적으로 구현되야함
컨트롤러 나누기
- 웹 어댑터는 한개 이상의 컨트롤러로 만들어도 됨
- 컨트롤러를 나눌수록 서로 독립적인 입출력 모델을 사용하게되고 독립적이게 됨
- 동시작업이 쉬워짐
유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?
- 웹 어댑터는 HTTP 요청 -> 유스케이스 요청 -> 유스케이스 반환 -> HTTP 반환 역할
- 즉, HTTP는 도메인 로직 없이, 변환만 해야됨
- 애플리케이션 계층은 HTTP와 관련된 작업을 하면 안됨 (독립적)
- 작은 클래스 만드는게 귀찮을 수 있지만, 유지보수에 빛을 발하리..
6장 영속성 어댑터 구현하기
의존성 역전
- 주도되는(driven) 아웃고잉 어댑터
- 호출만될뿐 호출하지는 않음 (외부 요인에 의해 작동하거나 영향을 받는)
- 계층간 의존성을 역전하여 서로 독립적인 개발이 가능
영속성 어댑터의 책임
- 영속성 어댑터가 하는일
- 입력을 받는다.
- 입력을 DB 포맷으로 매핑한다.
- 입력을 DB로 보낸다.
- DB 출력을 애플리케이션 포맷으로 매핑한다.
- 출력을 반환한다.
- 영속성 어댑터 입력 모델이 어플리케이션 코어에 있기 때문에, 어댑터를 변경하는 것이 코어에 영향을 미치지 않음
포트 인터페이스 나누기
- 넓은 포트 인터페이스를 사용하면 테스트 하기 어려움
- ISP 원칙에 따라 인터페이스를 분리
- 좁은 포트를 만들어서 플러그 앤 플레이 방식으로 개발을 할 수 있음
영속성 어댑터 나누기
- 영속성 어댑터는 도메인의 경계에 따라서 자연스럽게 분리
- 도메인 클래스 하나당 하나의 영속성 어댑터를 구현하는 방식
- 어그리거트 당 하나의 영속성 어댑터 접근방식 사용
- 바인디드 컨텍스트 영속성 요구사항을 분리하기위한 토대
- 바운디드 컨텍스트 끼리는 인커밍 포트르 통해 통신해야함
- 어그리거트는 바운디드 컨텍스트 하위에 속하는 개념
스프링 데이터 JPA 예제
- 매핑하지 않기 전략
- Account와 Activity를 연관관계 매핑하면 불러오기 편한데, 분리해서 호출함
- JPA는 기본생성자를 필요로 함 (불필요한 생성자 생성 필요)
- 항상 데이터의 일부만 가져오기 때문에, 연관관계 매핑이 불필요함
- 영속성 측면과의 타협 없이 풍부한 도메인 모델을 생성하고 싶다면, 도메인 모델과 영속성 모델을 매핑하는걸 추천
데이터베이스 트랜잭션은 어떻게 해야할까?
- 트랜잭션은 영속성 어댑터 호출을 관장하는 서비스에 위임해야됨
- Spring 라이브러리에 독립적이고 싶으면, AOP를 통해서 위빙(weaving) 하는 방법 추천
유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?
- 도메인 코드에 플러그인 처럼 동작하는 영속성 어댑터를 만들면 도메인 코드가 영속성과 분리되어 풍부한 도메인 모델 생성 가능
- 좁은 포트 패턴을 사용하면, 포트마다 다른 방식으로 구연하는 유연함이 생김
7장 아키텍처 요소 테스트하기
테스트 피라미드
- 테스트는 만드는 비용이 적고, 유지보수하기 쉽고, 빨리 실행되고, 높은 커버리지를 유지
- 여러개의 단위, 단위를 넘는 경계, 아키텍처 경계, 시스템 경계를 결합하는 테스트는 비용이 높음
- 단위 테스트 > 통합 테스트 > 시스템 테스트
- 단위 테스트
- 클래스를 인스턴스화하여 기능들을 테스트
- 다른클래스 의존이 있을 경우 mocking해서 테스트
- 통합 테스트
- 연결된 여러 유닛을 인스턴스해서 시작점이 되는 클래스가 데이터를 보내 테스트
- 두 계층 간의 경계를 테스트할 수 있기 떄문에, mocking해서 테스트
- 시스템 테스트
- end-to-end 테스트
얼만큼의 테스트가 충분할까?
- 얼마나 마음 편하게 배포할 수 있냐를 기준으로 삼자
유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?
- 육각형 아키텍처는 도메인 로직과 바깥으로 향한 어댑터를 깔끔하게 분리한다
- 도메인 로직은 단위테스트, 어댑터는 통합테스트로 정의 가능해진다.
- 좁은 포트를 이용할 수록, mocking이 시워지고 테스트하기 편함
- mocking이 어려울 경우, 분리해야하는 경고 신호가 될 수 있음
반응형