가끔 이러한 질문을 받을때가 있다. '저는 회사에서 Layered Architecture로 코드를 짜는데 굳이 Hexagonal Architecture을 알아야 할까요?' 나의 답변은 항상 일관되었다. 회사에서 Hexagonal Architecture을 쓰지 않더라도 Hexagonal Architecture의 철학과 효용성을 이해하고 다시 Layered Architecture를 본다면 코드를 보는 눈이 달라질 것입니다.
필자는 원래 iOS 개발자였다. 모바일 개발을 할때 다양한 아키텍처에 대해
학습을 하게 되었는데 그 중 많은 학습 시간을 들였던 MVVM 디자인 패턴을 기반으로한 Clean Architecture는 코드를 보는 눈을 한단계 향상시켜줬다.
규율과 원칙하에 코드를 작성해야 하는 이유들에 대해 배웠고 규율과 원칙하에 내가 작성한 코드가 잘못되었음을 알게되었다. 반성할 수 있게 되니 코드를 보며 생각이 더욱 깊어졌고 코드의 디테일을 보는 눈이 생겼다. 그렇게 하나씩 원칙을 지키고 수정하다보니 일관되어졌고 일관된 코드는 시간이 지난 내가, 다른 누군가 봤을때 큰 흐름을 이야기 해줬다. 그렇게 큰 문맥을 지닌 코드의 힘은 말로 설명할 수 없었다. 미래의 내가 편했고 미래의 누군가가 편했다. 그렇게 나는 아키텍처의 힘에 빠져들었다.
Spring 개발을 처음했을때 Layered Architecture를 직면했다. 의존성 주입을 통한 각 관심사에 따른 Layer의 단일 참조 철학은 MVVM을 기반으로 한 Clean Architecture와 비슷했다. MVVM 기반 Clean Architecture에서는 VM에서 비지니스 로직에 집중했고 Repository 영역은 Persistence 영역과 같이 Local Data, Server Data를 제공했다. Layered 아키텍쳐도 Service에 비지니스 로직을 집중했고 Persistance 영역에서는 데이터를 제공했다. 그러나 모바일의 환경과 서버 환경을 비교했을때 서버가 상대적으로 외부의 의존성에 대해서 더 많은 확장을 요구했다. 이러한 문제로 Service의 비지니스 로직이 점점 희석되어 간다는것을 깨닳았다. 즉, 보조로직이 비지니스 로직을 희석시키고 있었다.
비지니스 로직은 다양한 외부와의 복잡한 상호작용을 통해 만들어 진다. 외부의 API를 요청하여 결과 값을 받고 Persistence의 CRUD는 비지니스 로직을 위한 수단이지 그 자체가 비지니스 로직이 아니다. 작은 도메인에서는 그것이 상응할 수 있겠지만 큰 도메인과 여러 시스템이 공존하는 큰 시스템에서는 한계에 부딛힌다.
나는 이러한 문제를 해결하고 싶었다. 나의 바램은 Service Layer를 봤을때 비지니스의 흐름과 원리를 분명하게 파악하고 싶었다. 그렇게 나는 이러한 문제를 해결할 수 있는 방법들에 대해 연구했고 그 중 Hexagonal Architecture는 이 문제를 해결하기 위한 강력한 수단이였다.
Hexagonal Architecture 설명
- Domain / Business
- Hexagonal Architecture의 중심이다. Hexagonal Architecture의 원리와 원칙은 Domain / Business 영역을 중심으로 이뤄진다. 이 영역에서 작성되는 코드는 서비스의 비지니스와 관련된 원리와 흐름이 작성된다. 그 외 부가적인 것은 작성하지 않는다. Hexagonal Architecture에서 가장 지키고 싶고 소중히 여기는 부분이다. 우리도 소중히 여기자.- Adapter
- 외부와 상호작용을 하는 실질적 구현체이다. Endpoint Trigger, CRUD, 외부 API 호출 등... 비지니스 로직을 수행하기 위한 부가적인 로직은 모두 Adapter에서 이루어진다.- Port
- 비지니스 로직은 외부의 의존성에 의해 변질되어서 안된다. 그 변질을 막기 위해 Hexagonal Architecture는 Service와의 상화 작용은 반드시 Port를 통해서 상호작용 하도록 원칙을 제시하고 있다. 외부로 부터 유연한 변화와 대응을 가능하게 하는 중요한 연결 접합부다.- In / Out
- Domain / Business 영역을 중심으로 외부를 호출하는 것을 Out이라한다. 또한 Domain / Business 영역을 중심으로 안으로 들어오는 것을 in이라고 한다. 이렇게 탄생한 개념으로 in-adapter, in-port, out-port, out-adapter가 있다. 이렇게 만들어진 in-out의 개념을 통해 더욱 분명한 경계를 만들 수 있다. 이렇게 만들어진 분명한 경계는 체계적인 확장 가능함을 야기하고 코드를 작성하는 분명한 원칙을 제시한다.
위의 설명에서 알다 싶이 Hexagonal Architecture는 엄격한 규율과 규칙이 있다. 이러한 규율과 원칙은 반성하게 하고 일관되게 할것이다. 종합적으로 Hexagonal Architecture는 아래와 같은 특징을 지닌다.
Hexagonal Architecture 특징
- 확장성
- Port를 통해 외부의 변화에 대해 Domain / Business가 변질되지 않는다 했다. 또한 In-Out의 개념을 통해 명확한 기준으로 체계적으로 확장할수 있다고 했다. Hexagonal Architecture는 확장성에 강하다.
** 이해를 돕기 위해 예를 하나 들겠다. NotificationPort라는 out port가 있다. 이 out port는 Service의 Field이다. 추후 이 Port를 구현하는 구현체가 Spring Container에 의해 이 변수에 의존성 주입이 될것이다. 그리고 현재 이 Port를 구현하는 Out Adapter는 NotificationPersistenceAdapter이다. 즉 위에서 말한대로 이 PersistenceAdapter가 의존성 주입이 되어 비지니스 로직을 구성할것이다. 그런데 모종의 이유로 Notification이 서버로 분리됬다고 하자. 그리고 이 외부와의 상호작용을 하는 out Adapter를 NotificationServiceAdapter라 하자 이 친구는 Port를 구현한다. 아까와 똑같이 비지니스 로직은 NotificationServiceAdapter를 통해 로직이 구성이 된다. 그러나 Service 입장에서는 변하지 않는다... 즉 외부의 변화로 부터 안전하다. 왜 내가 이름을 NotificationPort라고 했는지 이해가 되는가? 이해가 되면 좋겠다 ㅎㅎ- 유지보수성
- Hexagonal은 Domain / Business, Adapter, Port, In-Out이라는 복합적 개념을 통해 명확한 원칙을 제시한다고 했다. 명확한 원칙은 명확한 방향성을 야기한다. 또한 일관성을 야기한다. 명확한 흐름과 일관성은 시간이 흐르고 사람이 바뀌어도 시스템을 유지할 수 있는 유지보수성을 야기한다.- 테스트 용이성
- Domain / Business를 지키는것이 Hexagonal Architecture의 중요한 원칙이라 했다. 또한 Domain / Business는 외부의 의존성으로 부터 자유롭다. 그러기에 테스트가 용이해진다. 테스트 용이성 외적으로 한가지 더 말하고 싶은 부분은 철저한 원리와 원칙을 통해서 명확히 경계를 나눈것이 HexagonalArchitecture이다. 즉 이렇게 나뉘어진 명확한 경계는 철저한 테스트를 야기한다.
'훌륭한 아키텍처는 코드를 보는 좋은 눈을 길러준다.' Hexagonal Architecture는 필자의 경험과 더불어 이 말의 힘을 실어준다.
'규율이 곧 자유다.'라는 훌륭한 말이 있듯이 '훌륭한 아키텍처를 지키는 것이 곧 자유다.'
최근에 MSA 프로젝트를 진행하고 있는데 다양한 시스템이 확장되고 상호작용 하는 MSA 프로젝트에서 더욱 안성맞춤이다.
다음 시간에는 실제로 Hexagonal Architecture를 통해 어떻게 코드를 작성하면 좋을지 같이 논의해보겠다.
감사합니다.