코드를 구성하는 몇 가지 방법을 소개한다.
소개할 예시들은 송금 서비스 BuckPal에 대한 것들이다.
특히 사용자가 본인의 계좌에서 다른 계좌로 돈을 송금할 수 있는 ‘송금하기’ 유스케이스에 대해 살펴본다.
buckpal
|
|--- domain
| |--- Acount
| |--- Activity
| |--- AccountRepository
| |--- AccountService
|
|--- persistence
| |--- AccountRepositoryImpl
|
|--- web
|--- AccountController
웹, 도메인, 영속성 계층 각각에 해당하는 전용 패키지인 web, domain, persistence를 둔다.
DIP를 이용해 의존성이 domain 패키지에 있는 도메인 코드만을 향하도록 한다.
단 이 패키지는 최적의 구조가 아니다! → Why?
buckpal
|
|--- account
|--- Acount
|--- AccountController
|--- AccountRepositoryImpl
|--- AccountRepository
|--- SendMoneyService
계좌와 관련된 모든 코드를 최상위 account 패키지에 넣었다.
계층 패키지들도 모두 없앴다.
패키지 외부에서 접근되면 안 되는 클래스들에 대해 package-private 접근 수준을 이용해 패키지 간의 경계를 강화할 수 있다.
AccountService → SendMoneyService로 네이밍 변경!
애플리케이션의 기능을 코드를 통해 볼 수 있게 만드는 것을 가리켜 ‘소리치는 아키텍처’ → 코드가 그 의도를 우리에게 소리치고 있기 때문
buckpal
|
|--- account
|--- adapter
| |--- in
| | |--- web
| | |--- AccountController
| |--- out
| |--- persistence
| |--- AcountPersistenceAdapter
| |--- SpringDataAccountRepository
|
|--- domain
| |--- Acount
| |--- Activity
|
|--- application
|--- SendMoneyService
|
|--- port
|--- in
| |--- SendMoneyUseCase
|
|--- out
|--- LoadAccountPort
|--- UpdateAccountStatePort
패키지의 의존성 방향이 adapter → application → domain 으로 흐른다.
헥사고날 아키텍처에서 구조적 핵심 요소 : 엔티티(도메인 엔티티), 유스케이스, 인커밍/아웃고잉 포트, 인커밍/아웃고잉 어댑터
위 패키지 구조에서는 각 아키텍처 요소들에 정해진 위치가 있다.
최상위에는 Account와 관련된 유스케이스를 구현한 모듈임을 나타내는 account 패키지가 존재
큰 구성
이 패키지 구조는 ‘아키텍처 - 코드 gap’을 효과적으로 다룰 수 있다!
패키지 구조가 아키텍처를 반영할 수 없다면 시간이 지남에 따라 코드는 점점 목표하던 아키텍처로부터 멀어지게 될 것이다.
어댑터 패키지의 모든 코드들은 application 패키지 내에 있는 포트 인터페이스를 통하지 않고서는 밖에서 호출되지 않는다.
application 패키지와 domain 패키지 내의 일부 클래스들은 public으로 지정해야 한다.
어댑터 코드를 자체 패키지로 이동시킴으로써, 하나의 어댑터를 다른 구현으로 필요에 따라 쉽게 교체 가능하다!
이 패키지 구조는 DDD 개념에 직접적으로 대응시킬 수 있다.
물론 완벽한 방법은 없다!
학습 난이도의 문제도 분명 존재한다고 생각한다.
또한 인커밍 포트의 경우 진입점이라는 아키텍처 상의 명목을 제외하고는 실제로 그 필요성이 의문이다.
클린 아키택처의 본질적인 요소 → 애플리케이션 계층이 인커밍/아웃고잉 어댑터에 의존성을 갖지 않는다!
근데 누가 포트 인터페이스를 구현한 실제 객체를 애플리케이션에 제공 하는가..?
모든 계층에 의존성을 가진 중립적인 컴포넌트를 하나 도입한다!
실제 코드 구조를 목표하는 아키텍처에 가깝게 만들어주는 헥사고날 아키텍처의 패키지 구조를 살펴봤다.
헥사고날 아키텍처의 패키지 구조를 사용함으로써, 의사소통, 개발, 유지보수가 모두 조금 더 수월해진다.