헥사고날 아키텍처를 보고 난 뒤 내가 느낀 점은
SOLID 에서 I 를 강조한 아키텍처라고 생각했다. (I 는 Inversion of Control 의존성 역전)
헥사고날 아키텍처는 레이어드 아키텍처의 일반적인 의존성 방향을 해결하기 위해 고안된 아키텍처이다.
아래 그림과 같다. 각각의 어댑터 구간이 포트를 통해 유스케이스와 연결되어 최종적으로 엔티티에 결합되는 구조이다.
헥사고날 아키텍처가 포트&어댑터 패턴이라고 불리는 이유가 여기있다.
어플리케이션 코어와 어댑터 간의 통신이 가능하려면 애플리케이션 코어가 각각의 포트를 제공한다. 이 때 driving 어댑터에게는 포트가 코어에 있는 유즈케이스 클래스에 의해 구현되어 호출되는 인터페이스가 되며, driven 어댑터에게는 포트가 어댑터에 의해 구현되고 코어에 의해 호출되는 인터페이스가 된다.
포트라는 인터페이스를 통해 내부와 외부의 경계를 나누는 것을 볼 수 있으며, 의존성 방향을 내부와 외부로 나눈 것도 하나의 특징이다. 이 헥사고날 아키텍처를 현업에서 가장 많이 쓰이는 Spring 에서 살펴보자.
일단 소스코드 구조 상으로는 아래와 같다.
유스케이스 (인터페이스, 구현체는 Service)
- 모델 상태를 조작한다
- 출력을 반환한다
- 비즈니스 규칙을 검증한다
- 웹으로부터 입력을 받는다
인터페이스로 존재하며, 구현체 Service 는 입력 포트와 출력 포트를 가지고 있다.
LoadEventPort, RecordEventPort 라는 웹 어탭터의 출력 포트를 통해 out/persistenceAdapter 와 통신하게 된다.
어댑터
웹 어댑터 (WebAdapter 어노테이션, Controller)
- HTTP 요청을 자바 객체로 매핑하기
- 권한을 검사하기
- 입력 유효성 검증하기
- 입력을 유스케이스의 입력 모델로 매핑하기
- 유즈케이스 호출하기
- 유즈케이스의 출력을 HTTP로 매핑하기
- HTTP 응답을 반환하기
영속성 어댑터
- 입력을 받는다
- 입력을 데이터베이스 포맷으로 매핑한다
- 입력을 데이터베이스로 보낸다
- 데이터베이스 출력을 애플리케이션 포맷으로 매핑한다
- 출력을 반환한다
Spring 에서 헥사고날 아키텍처의 흐름을 정리하면 다음과 같다.
Controller(어댑터) -> UseCase(인터페이스, 설마 Port ?) -> Service(구현) -> Port(인터페이스) -> Adapter(구현) -> Repository
원래 그림대로라면 Controller 와 UseCase 사이에 Port 가 존재해야 되는 것이 맞지 않나라고 생각했다. 아니 호출해야 되는 것이 맞다. 그런데 아래 내가 공부한 출처들에서 모두 생략하고 있다. UseCase 자체가 이미 인터페이스이기 때문에 의존성 역전이 이미 되어 있는 상태이니까 생략한 것이 아닐까라고 추측했다. 결국엔 포트를 둔다는 건 서로 다른 영역을 나누기 위한 경계 즉, 인터페이스로 의존성 역전을 한다는 의미일 것이니 말이다.
그런데, 그림을 보면 실체화하는 위치가 다르다. Driving 어댑터 쪽은 UseCase 가 실체화이고, Driven 어댑터 쪽은 Adapter 가 실체화이다. 이 구조를 Spring 구조와 비교해보면 UseCase 가 Input Port 라는 것을 알 수 있고 Service 가 사실상 저 그림에서 UseCase 가 되는 것이다. 혼동이 될 수 있는 여지가 있지만, Spring 에서는 전통적으로 저 방식으로 설계해왔기 때문에 Input Port 가 사라진 것처럼 보인 것이었다.
출처
Hexagonal Architecture with Java and Spring (reflectoring.io)
'독후감 > 교양' 카테고리의 다른 글
[TDD] 의식적인 연습으로 TDD, 리팩토링 연습하기 (0) | 2022.04.25 |
---|