DDD, Clean Architecture and Hexagonal 2
Published:
DDD를 더욱 발전시킨 ‘핵사고날 아키텍쳐(Hexagonal Architecture)’에 대해 알아보겠습니다.
핵사고날 아키텍쳐는 시스템의 유연성을 증가시키고 외부와의 의존성을 줄이는 방법론입니다.
핵사고날 아키텍쳐란?
핵사고날 아키텍쳐, 또는 포트와 어댑터 아키텍쳐는 응용 프로그램의 코어를 외부 요소로부터 격리시키는 디자인 패턴입니다. 이 아키텍쳐의 핵심은 비즈니스 로직을 중심으로 구성하고, 외부 시스템과의 상호작용은 ‘포트(Ports)’와 ‘어댑터(Adapters)’를 통해 이루어진다는 점입니다.
핵사고날 아키텍쳐의 장점
유연성과 확장성
- 시스템의 핵심 로직이 외부 변경으로부터 보호되므로, 새로운 기술이나 외부 시스템과의 통합이 용이합니다.
분리된 관심사
- 비즈니스 로직과 인프라스트럭처 코드가 분리되어, 코드의 관리와 유지 보수가 쉬워집니다.
테스트 용이성
- 핵심 비즈니스 로직을 외부 의존성으로부터 격리시킴으로써, 단위 테스트를 용이하게 합니다.
DDD와 핵사고날 아키텍쳐의 연결
DDD에서는 복잡한 도메인 로직을 효율적으로 관리하기 위해 유비쿼터스 언어, 애그리거트, 리포지토리 등을 사용합니다.
이와 같은 DDD의 구성요소들은 핵사고날 아키텍쳐의 중심부인 ‘코어(Core)’에 위치합니다.
반면, 외부 시스템과의 통신은 어댑터를 통해 이루어지고, 포트를 통해 코어와 연결됩니다.
커피숍에 예를 들어보면
매장 주문
- 코어 (Core): 커피숍의 핵심 비즈니스 로직, 즉 커피를 만들고 서비스하는 부분입니다.
주문 방식과 무관하게 일관되게 커피를 만들고 서비스하는 부분은 유지됩니다. - 포트 (Port): 고객이 매장에 들어와 직원에게 직접 주문하는 방식입니다.
이 포트는 고객의 요구사항을 코어로 전달하는 연결점 역할을 합니다. - 어댑터 (Adapter): 고객과 직원 간의 대화는 이 상호작용을 위한 어댑터입니다.
고객의 주문을 받아 코어(주방)에 전달하고, 완성된 커피를 고객에게 제공합니다.
드라이브 스루
- 코어 (Core): 여기서도 마찬가지로 커피숍의 핵심 비즈니스 로직이 중심입니다.
커피 제조 방식은 변하지 않습니다. - 포트 (Port): 드라이브 스루 창구입니다.
이 창구는 매장 내 주문과는 다른 방식의 상호작용을 제공하지만, 결국에는 동일한 코어 서비스(커피 제조)에 연결됩니다. - 어댑터 (Adapter): 창구를 통해 주문을 받는 과정이 어댑터의 역할을 합니다.
고객의 차량에서 주문을 받고, 이를 주방에 전달하여 최종적으로 커피를 고객에게 제공합니다.
핵사고날 아키텍쳐를 적용했을때의 장점
핵사고날 아키텍쳐의 적용된다면 이러한 구조에서, 핵사고날 아키텍쳐의 장점이 들어납니다.
- 유연성과 확장성: 새로운 주문 방식(예: 모바일 앱 주문)을 도입하려면 새로운 포트와 어댑터만 추가하면 됩니다.
이때 코어 비즈니스 로직은 변경할 필요가 없습니다. - 분리된 관심사: 주문 접수 방식과 커피 제조 프로세스가 분리되어 있어, 각각 독립적으로 관리 및 개선할 수 있습니다.
- 테스트 용이성: 각 컴포넌트(포트와 어댑터)를 개별적으로 테스트할 수 있어, 전체 시스템의 안정성을 보장할 수 있습니다.
이러한 구조는 소프트웨어 개발에서도 마찬가지로 적용되며, 각각의 컴포넌트가 특정 기능과 상호작용 방식을 담당함으로써 전체 시스템의 유연성과 확장성을 높여줍니다.
핵사고날 아키텍쳐의 단점 및 어려운 점
복잡성 증가
- 핵사고날 아키텍쳐는 여러 계층과 컴포넌트를 도입함으로써 시스템의 복잡성을 증가시킬 수 있습니다. 각 컴포넌트 사이의 상호작용과 의존성 관리가 더 복잡해질 수 있습니다.
초기 개발 비용과 시간 증가
- 분리된 컴포넌트를 설계하고 구현하는 과정은 추가적인 시간과 노력을 필요로 합니다. 초기 개발 단계에서는 이로 인해 시간과 비용이 증가할 수 있습니다.
유지보수의 어려움
- 각각의 포트와 어댑터가 독립적인 로직을 가질 경우, 시스템 전반에 걸친 변경 사항을 적용하는 것이 복잡해질 수 있습니다. 이는 유지보수 과정을 더 어렵게 만듭니다.
오버엔지니어링의 위험
- 핵사고날 아키텍쳐는 때때로 과도한 추상화와 복잡성을 도입할 위험이 있습니다. 이는 특히 작은 규모의 프로젝트나 간단한 애플리케이션에서 오버엔지니어링으로 이어질 수 있습니다.
러닝 커브
- 이 아키텍쳐를 적용하기 위해서는 개발자가 추가적인 설계 원칙과 패턴을 학습해야 합니다. 새로운 개발 팀원들이 이 아키텍쳐에 익숙해지기까지 시간이 걸릴 수 있습니다.
커피숍 시나리오에서의 어려움
매장 주문
- 복잡성: 매장 주문 시스템이 매우 다양한 고객 요구를 수용해야 할 경우, 포트와 어댑터의 설계가 복잡해질 수 있습니다.
- 유지보수: 주문 접수 방식에 대한 변경사항이 있을 경우, 이를 적용하기 위해 여러 어댑터를 일일이 수정해야 할 수 있습니다.
드라이브 스루
- 초기 비용: 드라이브 스루를 위한 별도의 포트와 어댑터를 구축하는 데 상당한 시간과 비용이 소요될 수 있습니다.
- 오버엔지니어링: 드라이브 스루 시스템이 복잡해지면서 실제 필요보다 과도한 기능을 구현하는 오버엔지니어링의 위험이 있습니다.