내부 영역 - 순수한 비즈니스 로직을 표현하며 캡슐화된 영역이고 기능적 요구사항에 따라 먼저 설계
외부 영역 - 내부 영역에서 기술을 분리하여 구성한 영역이고 내부 영역 설계 이후 설계
포트와 어댑터
개발을 시작할 때 포트와 어댑터의 역할, 두 어댑터간의 차이점을 제대로 인지한다면 개발이 수월합니다. (도메인 + application layer 까지 비즈니스 로직으로 입니다)
포트
Port는 application 입장에서 consumer, 또는 application에서 나가거나/들어오는 end point라고 볼 수 있다.
포트는 내부 비즈니스 영역을 외부 영역에 노출한 API이고 인바운드(Inbound)/아웃바운드(Outbound) 포트로 구분
인바운드 포트 - 내부 영역 사용을 위해 노출된 API
아웃바운드 포트 - 내부 영역이 외부 영역을 사용하기 위한 API
어댑터
어댑터는 인프라나 web과 같은 저수준 layer들이 도메인 로직에 접근할 수 있는 포트를 사용할 수 있도록 한다.
Primary (Driving) Adapters - 위의 다이어그램에서 왼쪽에 있는 adapter, application을 동작시키는(Driving) 역할을 한다. Driving Adapter에는 주로 UI 쪽이 들어간다. 인바운드 포트 사용
Secondary (Driven)Adapters - 오른쪽에 있는 adapter, application에 의해 동작되는(Driven) 역할을 가진 부분이다. 주로 인프라와 연결되는 부분이 들어간다. 아웃바운드 포트 사용
Primary adapter에 의해 Secondary adapter들이 호출된다. (인바운드 adapter, 아웃바운드 adapter로 부르기도 한다)
또한 두 종류의 adapter에서 어떻게 port와 adapter가 쓰이는지도 차이점이 있다:
Primary adapter는 port에 의존하고 있다. 실제로 사용될 때는 구체적인 port의 구현체가 adapter에 주입된다. 이 때 왼쪽에서 port와 그 구현체는 application 내부에 속하게 된다.
Secondary adapter는 port의 구현체가 되고 application의 비즈니스 로직에 주입된다. 그렇기 때문에 Application 비즈니스 로직의 입장에서는 port의 interface만 알고 있다. 이 때 port는 application 내부에 속하지만 port의 구체적인 구현체는 application 외부에 속하게 된다. 그리고 그 구현체는 외부 tool(SMS 라이브러리, ORM 등)을 감싸고 있다.
헥사고날 아키텍처의 장점
- 도메인 비즈니스 모델에 집중 DIP를 통해 의존성이 도메인에서 밖으로 나가는 부분이 없으므로 외부 요소를 신경쓰며 개발 할 필요가 없기 때문입니다.
- 모듈 일부를 배포하는 게 용이 기술과 실제 비즈니스로직의 분리, 각 도메인 별 비즈니스로직 분리를 통해 느슨한 결합을 가져가기 때문입니다.
- 기능 확장이 용이 원하는 기능에 대한 포트와 해당 포트를 사용할 어댑터를 추가해주면 됩니다.
- 쉬운 테스트 구성 모든 외부 기술들은 포트를 통해 비즈니스 로직과 연결되기 때문에, 본인의 역할을 수행하기 위해 필요한 Port만 사용하여 모킹어댑터를 통해 테스트를 쉽게 수행할 수 있습니다. 또한 내부 비즈니스 로직을 테스트할 때 외부에 의존성이 없기 때문에 모킹 필요성이 적어집니다.
- 개발비용이 감소 모든 의존성이 도메인을 향하기 때문에 계층간 의존성이 낮아지고 유연해지기 때문에 요구사항에 빠르게 대처할 수 있고, 테스트도 쉽게 적용할 수 있습니다.
- SoC(관심사 분리)의 장점 외부와의 연결에 문제가 생기면 Adapter를 확인하면 될 것이고, 인터페이스의 정의를 변경하고자 한다면 Port를, 마지막으로 비즈니스 로직이 제대로 동작하지 않는다면 도메인 로직만 확인하면 되기 때문에 결국 쉬운 테스트를 가능하게 해주기도 합니다.
헥사고날 아키텍처의 단점
- 코드가 많아진다. 도메인 계층이 영속성, UI같은 외부계층과 철저히 분리되어야 하기 때문에 엔티티에 대한 모델을 각 계층에서 유지보수 해야합니다. ex) ORM 프레임워크는 DB 구조 및 객체 필드와 칼럼의 매핑을 서술한 메타데이터를 담고 있는 엔티티 클래스를 필요로 합니다. 하지만 도메인 계층은 영속성 계층을 모르기 때문에, 두 계층에서 각각 엔티티를 만들어주어야 하고 도메인 계층과 영속성 계층이 데이터를 주고 받을 때, 두엔티티를 서로 매핑하는 과정이 생기게 됩니다 ( 도메인 계층과 다른 계층사이에서도 동일 ).
- 불필요한 오버헤드 아키텍처를 도입하기 전 포트,어댑터 등 알아야할 개념이 생기고, 아키텍쳐를 구현하기 위해서는 포트(인터페이스)를 생성해야 하고 도메인 모델의 여러 표현 사이를 매핑할 객체를 만들어야 합니다.