헥사고날 아키텍처 적용기(feat. 멀티모듈)

최인준·2024년 8월 8일
0
post-thumbnail

헥사고날 아키텍처란?

헥사고날 아키텍처는 소프트웨어 설계에 사용되는 아키텍처 패턴 중 하나이다.

클린 아키텍처를 좀 더 구체화한 것이고 도메인의 유지보수성 향상을 가장 중요한 가치를 두는 아키텍처로 도메인을 외부 즉, 프레임워크나 웹계층, 영속성 계층에서 오염시키지 않도록 하는데 목적을 두고 있다.

  • DRIVING SIDE : 이쪽의 포트는 코어에 있는 유스케이스에 의해 구현된다.
  • DRIVEN SIDE : 이쪽의 포트는 어댑터에 의해 구현되고 어댑터에 의해 호출된다.

즉, DRIVING SIDE는 인고잉, DRIVEN SIDE는 아웃고잉 어댑터가 위치해있다.

헥사고날 아키텍처에 대해 깊게 다루진 않을 것이다. 이번 포스팅에서는 우리 팀에서 어떻게 적용 했는 지 그 과정을 보여주고 깃헙도 첨부하고자 한다.

일단 우린 Kotlin 을 사용하기로 했고 멀티모듈을 활용해 헥사고날 아키텍처를 설계하기로 하였다.

모듈 구성

우리는 헥사고날 아키텍처를 도입하기 위해 크게 총 4개의 모듈이 있다.











이렇게 총 4개의 모듈이 있고 adapters 모듈에는 4개의 모듈이 하위 모듈로 더 구성 되어있다.
지금은 하위 모듈이 4개이지만 어댑터가 더 추가되는 상황이라면 adapters 모듈안에 하위 모듈을 추가하면 된다.

adapters:in-web 모듈

adapters 모듈부터 보자면 첫번째로 in-web 모듈이 있다.

in-web은 인고잉 어댑터로 필요한 api들이 구현되어 있다. in-web모듈에서는 Input Port를 호출해야 하기 때문에 application 모듈을 참조하고 있고 dto를 참조하기 위해 domain모듈도 참조하고 있다.

in-web build.gradle.kts

dependencies {
    implementation(project(":domain"))
    implementation(project(":application"))
		//...라이브러리들
}

adapters:out-cache 모듈

이 모듈은 Redis 기술을 구현한 어댑터를 위함을 목적으로 만들었다.

진행중인 프로젝트에 경우 매일 매일 다른 컨텐츠를 사용자에게 추천 해줘야 하는 요구사항이 있어 해당 기술 구현을 이 모듈에서 진행하게 될 것이다. (아직 구현 전ㅎ)

이 모듈은 Output Port를 구현해야 하기 때문에 Port가 위치한 application 모듈을 참조한다.

위와 동일하여 gradle 파일 코드는 생략하겠다.

adapters:out-persistence 모듈

프로젝트에서 ORM 기술로 JPA를 사용한다. 이 모듈에서는 JPA 관련 기술이 활용된다.

역할은 다음과 같다.

  • JPA Entity 구현
  • DB 관련 Output port interface 구현(Adapter)

프로젝트에서는 도메인 모델과 JPA Entity를 분리했고 그 변환 작업 또한 이 모듈에서 진행하였다.

특정 도메인 패키지 내부는 또 impl과 persist로 나뉘게 된다.

persist는 JPA 관련 기술이 들어간 클래스가 정의되어 있고 impl은 JPA 관련 클래스를 활용하여 Output Port를 구현한다.

adapters:out-web 모듈

out-web에서는 외부 API를 호출하는 작업을 수행한다. 네이밍 그대로 in-web과 반대되는 것으로 in-web은 클라이언트측이 우리 web을 호출하는 작업을 수행하지만 out-web에서는 우리가 외부 API를 호출하는 작업을 수행한다.

프로젝트에 소셜로그인 요구사항이 있는데 구글, 애플 관련 API를 호출한다. 소셜로그인 외에도 최근에 FCM을 구현하고 있는데 이도 구글 API를 호출해야하기 때문에 해당 모듈에서 구현하고 있다.

application 모듈

헥사고날 아키텍처를 그림으로 나타냈을 때 코어를 담당하는 모듈이다. 역할은 다음과 같다.

  • in and out port 인터페이스 정의
  • Use Case 구현(응용 서비스)

여기서 Input Port는 application 모듈에서 호출하고 Output Port는 in-web모듈에서 호출한다.

헥사고날 아키텍처의 특징 중 하나는 모든 의존성이 가운데 즉, 도메인층을 향한다는 것이다.

그렇기 때문에 application모듈에서는 domain 모듈 하나만 참조한다.

dependencies {
    implementation(project(":domain"))
    //..라이브러리들
}

domain 모듈

해당 모듈은 도메인 모델과 dto만을 정의한다. 물론 도메인 로직도 포함이다.

그렇기에 POJO로 된 모듈이며 build.gradle.kts에도 어떤 모듈, 라이브러리도 의존하지 않는다.

우리는 예외처리를 ErrorCode 인터페이스를 두어 도메인 별로 관리하게 만들었기에 예외 관련 클래스들도 domain 모듈에 두었다.

entry 모듈

실제로 빌드 될 메인 클래스가 있다.

모듈안에 web 모듈이 또 따로 있는 이유는 한 프로젝트 내에 메인 서버 하나만 있는게 아니기 때문이다.

지금은 하나지만 우리팀은 알림 관련 요구사항으로 인해 배치 서버가 따로 필요하게 되었다.

배치 서버를 빌드하는 클래스를 위해 entry 밑에 모듈을 또 만들예정이다.

의존 방향 정리

adaptersapplicationdomainentry:webentry:batch
adapters-OOXX
applicationX-OXX
domainXX-XX
entry:webOOO-X
entry:batchOOOX-

적용해보며

지금 프로젝트를 계속 진행중이지만 몇가지 체감되는 장단점이 있었다.

먼저 단점이 제일 빨리 느껴졌는데 계층형 아키텍처와 비교해보면 코드양이 몇배는 더 많다…

프로젝트 초반부에는 클래스 무한 생성을 하며 단축키 빨리 치는 속도는 많이 늘었다😀ㅋㅋ

장점으로는 DDD를 적용하기 좋은 구조라 재미를 느낄 수 있다는 것이다.(약간의 재미)

또한 프로젝트 진행도 중반부를 넘어가면서부턴 아키텍처에 익숙해지고 유지보수성이 좋은 프로젝트가 되어간다는 것이 느껴졌다.(기능 변경사항이 많아지면 어댑터를 단순히 갈아끼우기만 하면 된다거나 하는 식으로)

또한 개발을 하면서 개념적으로 분리가 딱딱 이루어진 느낌이라 머리가 복잡해지는 상황이 없었다! 물론 요구사항 자체가 복잡한 상황은 제외하고이다ㅎㅎ.

헥사고날 아키텍처는 이론에서 그치지 않고 직접 경험해보는 것이 많은 도움이 될 것이라 생각하고 그 경험을 시작하며 내 글을 보고 도움을 받는 사람이 있으면 좋겠다!

아키텍처 적용하여 진행중인 프로젝트 레포를 공유하며 마치도록 하겠다.

깃헙 레포

0개의 댓글