Develop/Design

[Clean Architecture] 육각형 아키텍처 (헥사고날 아키텍처) 패키지 구조

코딩의성지 2022. 4. 12. 01:18

지난번 포스팅에서 간단하게 육각형 아키텍처에 대해 포스팅을 했었다.

https://devkingdom.tistory.com/341?category=838914 

 

[Clean Architecture] 계층형 아키텍처의 문제점을 해결하는 육각형 아키텍처(헥사고날 아키텍처)

얼마전에 계층형 아키텍처의 문제점에 다뤘었다. https://devkingdom.tistory.com/340 [Clean Architecture] 계층형 아키텍처의 문제점 최근에 "만들면서 배우는 클린 아키텍처"라는 책을 정독했다. 요즘 웹 아

devkingdom.tistory.com

 

오늘은 육각형 아키텍처를 설계할 때 패키지 구조를 어떻게 잡아야할 지에 대한 포스팅을 해보려 한다. 

 

육각형 아키텍처 구조에서 핵심적인 부분은 엔티티, 유스케이스, 인커밍/아웃고잉 포트, 인커밍(주도하는)/아웃고잉(주도되는) 어댑터이다.

육각형 아키텍처의 이상적인 패키지 구조

 

아래의 패키지 그림을 보자.

위의 패키지 구조는 Account 와 관련된 유스케이스를 구현한 모듈인 account 패키지이다.

 

그 아래 레벨로 domain, adapter, application 패키지가 존재한다.

 

domain 패키지는 말그대로 도메인 모델이 속해 있는 패키지이다.

 

application 패키지는 도메인 모델에 대한 비지니스 로직을 수행하는 서비스 계층이 포함된다.

 

application/service 패키지 내부를 보면 GetAccountBalanceService와 SendMoneyService는 인커밍 포트 인터페이스인 GetAccountBalanceQuery 와 SendMoneyUseCase를 각각 구현하고,

GetAccountBalanceService에서는 영속성 어댑터에 의해 구현된 LoadAccountPort를 SendMoneyService에서는 마찬가지로 영속성 어댑터에 의해 구현된 LoadAccountPort, AccountLock, UpdateAccountStatePort, MoneyTransferProperties를 사용한다.

 

adapter 패키지는 어플리케이션 계층의 인커밍 포트를 호출하는 인커밍(웹) 어댑터와 아웃고잉 포트에 대한 구현을 제공하는 아웃고잉(영속성) 어댑터를 포함하고 있다.

 

이러한 패키지 구조는 대부분의 프로젝트에서 아키텍처가 코드에 직접적으로 매핑될 수 없는 추상적인 개념이라는 사실을 보여주면서 '아키텍처-코드 갭' 혹은 '모델-코드 갭'을 효과적으로 다룰 수 있게 해준다.

 

위의 구조처럼 패키지가 아무리 많아도 모든 것을 public으로 허용할 필요도 없다.

모든 클래스들이 application 패키지 내의 포트 인터페이스를 통해 바깥에서 호출되기 때문에 어플리케이션에서 어댑터 클래스로 향하는 우발적인 의존성은 존재할 수가 없다.

그렇기 때문에 대부분의 클래스를 package-private 접근 수준으로 둬도 된다.

 

하지만 application 패키지와 domain 패키지의 몇몇 클래스들은 public으로 지정을 해줘야한다. 

의도적으로 어댑터에서 접근해서 사용해야하는 포트 클래스는 public이어야하고,

도메인 클래스들은 서비스나 어댑터에서도 접근이 가능하도록 public 이어야 한다.

 

조금 어렵고 이해가 잘 안되실수도 있다. 하지만 이렇게 구조를 만들어두면 장점이 꽤나 많다.

우선 어댑터를 쉽게 쉽게 교체할 수가 있다. 예를 들어 특정 데이터베이스를 연동해 사용하다가 데이터 베이스를 변경해야한다고 생각해보자. 이때 단순히 아웃고잉 포트들만 새로운 어댑터 패키지에 구현하고 기존 연동된 부분을 지워주기만 하면된다.

 

그리고 또 하나의 장점은 DDD 를 꽤나 구조적으로 구현할수 있다는 것이다. DDD에서는 바운디드 컨텍스트라는 개념이 나오는데 여기서는 account 패키지가 다른 바운디드 컨텍스트와 통신할  바운디드 컨텍스트가 된다.

 

의존성 역전

오늘 포스팅한 내용의 핵심은 어플리케이션 계층이 인커밍/ 아웃커밍 어댑터에 의존성을 가지지 않는것이다.

패키지를 보면 말겠지만 인커밍(웹) 어댑터는 제어 흐름의 방향이 어댑터-도메인 코드간의 의존성 방향과 일치하여 따로 생각할게 없다. 단지 어댑터가 어플리케이션 계층에 위치한 서비를 호출하면 된다.

 

반명 아웃고잉(영속성) 어댑터는 의존성 역전 원칙을 적용해줘야한다.

위의 패키지에서는 아래 그림처럼 어플리케이션 계층에 아웃고잉 포트 인터페이스를 두고 영속성 어댑터가 이를 구현하게 함으로써 의존성을 역전 시켰다.

 

 

 

그리고 스프링에서는 이런 포트 인터페이스들을 직접 수동으로 초기화 하진 않는다. 이 부분에서 의존성 주입이 활용되는데 스프링에서는 IoC 컨테이너가 그역할을 해준다.

 

오늘은 정말 쉽지 않은 개념이지만 잘 이해만 해둔다면 정말 좋은 아키텍처를 설계할 수 있는 기반이 될 것이다. 이해가 될때까지 읽어서 꼭 이해해두기 바란다. 끝

 

Ref.
톰 홈버그, 『만들면서 배우는 클린 아키텍처』, 위키북스(2021), p27~32

반응형