스프링 부트의 멀티모듈 구현 도전욕구가 생겨 진행했던 일부 기록을 남기고자 합니다.
첫 번째 설계는 간단하게 도메인 모듈과 Client 모듈을 분리하는 것으로 결정했습니다. 이러한 결정을 하게 된 이유는 크게 두가지 인데요.
DB를 마이그레이션 하는 케이스가 그렇게 많지 않다는 점과, 테스트의 용이성 때문이었습니다.
DB 마이그레이션을 진행할 때 고려해야 할 사항도 굉장히 많기 때문에 상당히 보수적인 스탠스를 취할 것이라고 판단을 했습니다. 추가적으로 모듈화로 인한 운영 및 관리의 어려움과 모듈화를 함으로서 제공할 수 있는 이점을 저울질해보니 분리를 하지 않는게 나아보였습니다.
멀티모듈로 전환하면서 어려웠던 점이 통합테스트의 어려움이었습니다.
SpringBoot를 기준으로 설명해드리자면, 우리가 일반적으로 사용하는 Bean 주입이 생각처럼 되지 않습니다. 그래서 통합 테스트의 용이성을 위해서 분리를 하지 않기로 결정했습니다.
이렇게 하나의 프로젝트를 두개의 모듈(Client
, Domain
)로 나눈 후 프로젝트를 진행해나갔습니다.
프로젝트를 진행하다보니 각각의 도메인이 사용하는 DB가 동일한 설정을 가지는 것을 발견하게 되었습니다. 여러개의 DB를 가져야 하는 케이스도 있었고요. 이런 경우면 DB(Infrastructure) 역시 모듈화를 통해 사용하는게 좋아보입니다!
그래서 이렇게 계층별로 모듈화가 된 아키텍처가 완성되었습니다.
모듈화를 진행하면서 의존성을 어떻게 설정해야 할지에 대해 고민이 들었습니다.
각자 제공을 하는 API를 노출시킨 후 이를 의존함으로 각 모듈의 구현체에 직접 의존하지 않게 하는 아키텍처를 설계했습니다.
위의 구조를 사용하다 보니 불편한 점이 있었는데 바로 Domain 모듈이 Infra의 인터페이스 Spec에 영향을 받는다는 점이었습니다. 현재 저의 모듈 구상은 Domain이 핵심 모듈이기 때문에 Infra와 같은 Low Level 모듈에 의해 변경점이 발생하는걸 원하지 않았습니다. 추가적으로 회원 Repository 는 외부 도메인에서 사용하지 않고, 회원 도메인에서만 제공하기 때문에 Domain 에서 인터페이스를 제공하고 실질적인 구현을 Low Level 구현부에 맡기는 것이 더 유연한 아키텍처가 될 것이라 판단했습니다. 이러한 아키텍처를 보통 헥사고날 아키텍처라고도 하죠.
이렇게 되면 이제 Domain, Infra의 의존성 역시 역전이 됩니다.
// domain build.gradle
dependencies {
implementation project.parent.parent.project("infra-mysql")
}
// infra build.gradle
dependencies {
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.projectlombok:lombok:1.18.28'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
}
이 아래와 같이 변경됩니다. domain
에서는 아무 의존도 받을 필요가 없게 됩니다. 덕분에 domain 은 가장 순수한 형태를 유지할 수 있게 되었군요.
// domain build.gradle
dependencies {
// domain은 아무 의존성도 필요가 없다!
}
// infra build.gradle
dependencies {
implementation project.parent.parent.project("domain") // 의존성 역전!
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.projectlombok:lombok:1.18.28'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
}
만약 해당 Infra 모듈이 다른 모듈으로 부터 재사용이 된다면 아마 1번과 같이 Infra Module가 인터페이스를 제공하는게 더 타당한 구조라 생각됩니다. 아래의 사진처럼 말이죠.
아마 이런 구조라면 Infra 쪽에서 어느 도메인에도 영향을 받지 않게 직접 인터페이스를 제공하는게 더 좋겠죠?
아마 Common Module(공통 모듈)들이 이러한 형태를 취할 것 같습니다.
모듈을 어떻게 분리시킬것이가? 에 대한 아이디어를 잘 설명해주신 영상입니다.
https://www.youtube.com/watch?v=nH382BcycHc&t=4514s