Dev/Architecture

헥사고날 아키텍처(Hexagonal Architecture) 쉽게 알기

린네의 2024. 5. 29. 19:04

 

이전 MSA에 대해서 리뷰 했는데, 이번 게시글은 헥사고날 아키텍처다. 해당 아키텍처가 어떤 경우에 선택되어야 하는지 공유하면 좋을 것 같아서 글을 작성해 본다.

 

헥사고날에 대해 알기 전에, 전통적인 아키텍처에 대해 먼저 정리하고자 한다.

 

계층형 아키텍처(Layered Architecture)

 

가장 대중적으로 사용되는 전통적인 아키텍처로 비즈니스 로직, 데이터 액세스, 프레젠테이션 계층으로 구성된다. 일반적으로 많이 사용하는 repository(database, persistenece layer), service(domain layer), controller(presentation layer)를 생각하면 쉽다.

 

같은 목적의 코드들을 같은 계층으로 그룹화한 것으로, 역할과 관심사를 기준으로 계층을 분리한다.

 

[출처] https://blog.kyobodts.co.kr/2022/01/26/aws-web-application-3tier-architecture/

 

 

 

 

[출처] https://overcoded.dev/posts/Arch-14

 

 

이러한 계층형 아키텍처는 몇 가지 문제점을 가진다.

 

  • 계층형 아키텍처의 문제점

계층형 아키텍처를 사용하면 'db/persistence ->  business -> presentation'의 흐름으로 구성되는데, 이것은 자연스럽게 데이터에 의존하게 만든다. 모든 것이 데이터베이스의 설계를 시작으로, 데이터베이스가 가진 데이터를 기준으로 만들어지는 데이터베이스 주도 설계가 되는 것이다. 이렇게 되면 중심이 되어야 할 비즈니스로직이 우선시되는 게 아니라 데이터베이스에 맞춰서 비즈니스와 프레젠테이션 레이어가 완성되는 문제점을 야기한다.

 

가장 와닿는 예시라고 하면 ORM을 사용할 때가 있다. ORM을 사용해서 관리되는 엔티티들은 일반적으로 영속성 계층에 주로 위치하는데, 도메인 계층에서 엔티티에 접근이 가능하기 때문에 서비스가 이런 엔티티를 사용하는 경우가 생긴다. 이것은 곧 영속성 계층과 도메인 계층 사이에 강한 결합이 생기는 것을 의미한다. 가령 엔티티가 변경된다면, 비즈니스 로직도 변경되어야 한다.

 

또한 계층을 건너뛰는 구현을 하는 문제가 생긴다. 가령 Controller(Presnetation)에서 Repository를 호출한다던지 하는 문제다. 이것은 당장은 소스 코드의 작성을 감소시켜 속도가 빠를 수 있지만, 나중엔 계층별 역할의 분리가 모호해지고 이는 서비스 확장과 유지보수의 불편함으로 이어진다. 

 

 

 

헥사고날 아키텍처(Hexagonal Architecture)란?

헥사고날 아키텍처는 사전적 의미로 육각형 형태로 디자인된 아키텍처 형태 띄는 건축물을 의미한다. 비즈니스 로직을 외부 라이브러리 및 툴로부터 분리할 때 포트와 어댑터라고 부르는 인터페이스를 사용하기 때문에 포트&어댑터 아키텍처라고 표현되기도 한다. 도메인 비즈니스 로직이 외부 요소에 의존하지 않게 만들고, 프레젠테이션 계층(controller)과 데이터 소스 계층(persistence) 같은 요소들이 같은 도메인 계층에 의존하도록 하는 것을 목표로 한다. 즉, 의존성의 방향이 중앙을 향해 있는 아키텍처라고 할 수 있다.

 

[출처] Spring Boot and Hexagonal Architecture

 

 

 

위 사진처럼 핵심 비즈니스 로직은 중앙의 도메인 영역에 위치시키고 입력과 출력을 처리하는 포트와 어댑터를 밖에 배치하여 외부와 소통할 수 있다.

 

포트와 어댑터는 각각 In/Out로 분리되는데 각각 하는 역할은 다음과 같다.

 

  • Adaptor

애플리케이션 코어(핵심 도메인)와 외부 세계를 연결한다. 어댑터는 특정 외부 기술이나 프레임워크에 의존적인 로직을 담당하며, 이를 통해 외부와의 결합도를 최소화하고 어댑터를 통한 교환 가능성을 확보한다.

In Controller, 주로 사용자 인터페이스, 테스트 또는 외부 시스템으로부터 요청을 애플리케이션의 코어로 전달하는데 사용함
Out Jpa repository, 애플리케이션 코어에 외부에 데이터를 전달하는 역할을 담당한다. 데이터베이스에 데이터를 저장하거나 외부 시스템에 메시지를 전송하는 등의 역할을 한다 
  • Port

애플리케이션 코어의 경계를 정의하며, 애플리케이션 코어가 제공해야 할 기능을 나타낸다. 어댑터를 통해 애플리케이션 코어에 접근하는 인터페이스이다.

In Controller와 Service사이의 인터페이스, 외부 요청이 애플리케이션 코어로 들어오는 경로를 정의함
Out Repository와 Service사이의 인터페이스, 애플리케이션 코어가 외부 세계에 서비스를 제공하기 위한 경로를 정의함 
  • Use Case

외부로부터 들어오는 도메인을 이용해 요청사항을 처리함. 즉 애플리케이션의 비즈니스 로직을 나타냄. 

  • Domain

외부로부터 받은 요청을 처리하는 메인 비즈니스로 헥사고날 아키텍처의 가장 가운데 위치함. 외부에 의존성이 없는 POJO 형태로 구성

 

 

 

  • 헥사고날 아키텍처 장점
  1. 아키텍처 확장이 용이하다
  2. SOLID 원칙을 쉽게 적용할 수 있다
  3. 모듈 일부를 배포하는 것이 용이하다
  4. 테스트를 위해 모듈을 가짜로 바꿀 수 있으므로 테스트가 더 안정적이고 쉽다
  5. 더 큰 비즈니스적 가치를 갖고 더 오래 지속되는 도메인에 큰 관심을 둔다
더보기

cf.  OOP에서 SOLID 원칙

1. 단일 책임 원칙 - SRP : 클래스는 단 하나의 책임(기능)만 가져야 한다.  

2. 개방 폐쇄 원칙 - OCP : 확장에 열려있어야 하며, 수정에는 닫혀 있어야 한다. 추상을 통한 관계 구축을 이용하는 것이 이에 해당한다.

3. 리스코프 치환 원칙 - LSP : 서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다는 의미이다.  다형성을 이용하기 위한 원칙으로 업캐스팅된 상태에서 부모의 메서드를 사용해도 동작이 의도되로 흘러가야 하는 것을 의미한다. 

4. 인터페이스 분리 원칙 - ISP : 인터페이스를 각각 사용에 맞게 끔 잘 분리해야 한다는 원칙이다. 인터페이스의 단일 책임을 강조한다. 

5. 의존 역전 원칙 - DIP : Class를 참조해서 사용해야 하는 상황이 생긴다면, 그 Class를 직접 참조하는 것이 아니라 대상의 상위요소로 참조하라는 원칙이다. 구현 클래스가 아니라 인터페이스에 의존하도록 하는 것을  의미한다.  ( 인터페이스에 의존하는 이유는 자주 변화하는 것보다 변화가 거의 없는 것에 의존하는 것이 좋기 때문이다. ) 

 

  • 헥사고날 아키텍처 단점
  1. 소스가 많아진다. 최소 포트와 어댑트 두 계층으로 나눠어서 구성하므로 간단한 기능을 구현할 경우 이에 대한 비용 발생에 대해 고려해야 한다.
  2. 복잡하다.

 

 

 

여기까지 이론적인 측면에서 헥사고날에 대해 정리해 봤다. 좀 더 직관성 있는 설명을 위해 다음엔 예제 코드와 함께 작성해볼예정이다.