🧠 개념
핵사고날 아키텍처 (소프트웨어 디자인)
외부 종속성을 제거하기 위한 아키텍처 방법론

특징
포트와 어댑터 패턴을 사용하여 각 레이어 간의 의존성 방향을 제어한다. (소프트웨어 디자인)
- 모든 의존성은 도메인 중심으로 안쪽을 향함 (Dependency Rule)
- 외부 레이어는 내부 레이어의 인터페이스를 통해서만 통신
- 각 레이어는 독립적으로 테스트 및 교체 가능
전통적인 레이어드 계층을 대안책중 하나이다
사용 목적
- 각 컴포넌트간 느슨한 결합을 위해
얻는 효과
- 컴포넌트간 쉽게 교환가능하게 만든다
- 순수한 비즈니스 어떤 관계에도 종속되지 않는 코드가 된다
- 유지보수가 향상된다
외부 의존성 문제 예시
예를 들어서 “밥을 한다”라는 동작
- 쌀을 3그릇을 담는다
- 쌀을 씻는다
- 압력받솥에 취사 버튼을 누른다
여기서 “압력받솥에 취사 버튼을 누른다” 이 압력밥솥 ‘쿠쿠’ 브랜드에 있는데?
다른 브랜드로 교체하면서 취사 버튼이 없고 다른게 있다면? 밥을 한다 라는 동작이 영향을 받는다.
특정 제품에 종속되어 버린다.
제품이 바뀌면 밥을 못짓나?
그러면 안된다
살기 위해서는 밥을 먹어야한다
예를 들어서 “밥을 한다”라는 동작
- 쌀을 3그릇을 담는다
- 쌀을 씻는다
- 쌀을 익힌다
압력밥솥이 없어도 , 취사버튼이 없어도 밥을 만들어서 먹을 수 있다.
즉 도구를 어떤걸 쓰든 쌀을 익혀야 밥이 된다. 이런 의미로 코드를 작성하여 외부 도구에 종속되지 않고도, 바뀌어도 핵심 동작은 그대로이게 된다.
🔍 원리
핵심 원리
서로 다른 경계에 있는 컴포넌트는 반드시 port라는 출입구를 통해 들어오고 나갈 수 있도록 강제하여 외부로부터의 종속성을 제거하는 원리이다.
즉, 포트를 통해서만 들어오고 나갈 수 있도록 하여 특정 기술에 종속받지 않게 하는 의도이다.
그 결과 최종적으로 application 계층 → 요구사항을 나타낸다. (순수 그 잡채)
그럼으로써 기술이 바껴도 요구사항은 변하지 않는다.
http가 다른 특정 기술로 대체되면 그걸로 adapter만 똑 바꾸면 된다.
db가 Mysql 에서 Postgres로 바꼈다? adapter에 Out에 위치한 컴포넌트만 똑 바꾸면 된다.
절대로 특정 기술에, 제품에 종속되지 않는다.
시간이 지나 요구사항은 업그레이드 될 수 있지만, 본질은 절대 변하지 않게 된다.
즉 외부 요인에 의해서는 절대 변하지 않는다.
패키지 구조
{domain_name} | --- adapter => 입력 컴포넌트 및 출력 컴포넌트 | --- domain => 핵심 비즈니스 도메인 객체 | --- application => 요구사항 비즈니스 로직 수행
상세 패키지 구조 설명
adapter: 외부로 들어오는 또는 나가는 컴포넌트들의 집합
adapter/in : 외부로 부터 들어오는 컴포넌트
adapter/out : 외부로 부터 나가는 컴포넌트
domain : application 내의 비즈니스 로직에서 모델 객체
핵심 자원 이다.
라이프 사이클 영역: application
application: 요구사항 정의서이자 비즈니스 로직이 수행되는 컴포넌트 공간
application/port/in: 핵심 비즈니스 컴포넌트로 들어오는 입구
application/port/out: 핵심 비즈니스 컴포넌트로 나가는 출구
패키지 구조에서의 핵사고날 특징
- adapter는 외부 의존성 컴포넌트
- 이 외부 컴포넌트는 특징이 있다.
- 들어오는 혹은 나가는 역할만을 한다는 특징이다.
- 그래서 In/out으로 구분된다고 생각한다
- 즉 들어오는 건 들어오기만하고, 나가는 건 나가기 만한다 특징이 있다
그림으로 원리 이해하기 

핵심 동작 원칙
- 의존성 규칙: 모든 의존성은 안쪽(application 도메인)을 향해야 함
- 계층 간 통신: 인터페이스(포트)를 통해서만 가능
- 교체 가능성: 외부 기술은 언제든 다른 구현체로 교체 가능
🚨 주의 및 한계
전달하기 위한 객체 범위 문제
각 경계를 구분짓기 위해 각 경계에서만 사용하는 모델을 어떻게 제한 할 것인가?
- 공통으로 사용하는 객체
- 공통 get 프로퍼티로 강제하여 하나 처럼 사용하기
- 각 레이어에 맞는 dto로 변환하여 넘겨주기
1단계에서 3단계로 갈수록 보일러 플레이트 코드 발생.. 유지보수성이 안좋아지는 한계가 있다.
이 문제는 특별히 가이드되고 있지 않다.
그래서 세가지 중 하나를 선택해서 사용하면 된다.
개인적인 의견
- 3번으로 하는것이 외부 의존성을 제거하는데 맞다고 생각한다.
- dto에 의해 의존이 달라진다고 생각한다.
- 처음에는3번이 가장 이상적이지만.. 비슷한 DTO 객체가 생기는 문제가 있어 2번이 적절한것같다 고 생각했다
- 하지만 java언어를 사용하는 나로서는 record 객체로 불변성을 확보하고자 하는데, get 프로퍼티를 만들어줘야 하는.. 단점이 생긴다. 차라리 이럴거면 class 로 선언하는데.. 양갈래길이 하나 더 나온다..
- 변환하고 각 경계에 맞는 모델을 두어 사용한다면, 핵사고날에서 목표하고자 한 외부 의존성 제거와 부합한다고 생각한다
📝 요약
핵사고날은 외부 종속성을 제거하기 위한 아키텍처 방법론 이다.
서로 다른 컴포넌트간 통신하기 위해서는 반드시 port라는 출입구를 통해 전달받도록 강제하여 외부로 부터의 종속성을 제거한다.
그럼으로써 요구사항 글이 코드로 옮겨져 곧 application 요구사항 정의서가 된다.
방대한 코드 영역을 잘 효율적으로 다루기 위해서 관계를 느슨하게 만드는 것이 핵사고날에서 목표하고자 하는 바이다.
🤔 사용하면서 느낀 점
왜 테스트는 구현체에다가 하는지 의문이 들었다.
여러 포스팅된 글에서 테스트를 구현체에다가 한다?
대체 왜지?
테스트도 코드인데?
테스트도 결국 의존성에 구애받기 때문에 유지보수가 어려워질수 있다고 생각했기 때문에 인터페이스 자체를 테스트 해야하는 거 아닌가? 라는 생각이 들었다.
그래서 개인적으로 특정 persistence기술이 바뀌어도 해당 부분만 변경하기 위해서 인터페이스에 대한 테스트를 작성하였다.
main 패키지

test 패키지

🔗 관련 링크: → 공식 문서, 영상, 다른 사람 정리

플레이 스쿼드 헥사고날 아키텍처 적용기
시작하며