Spring Modulith로 모놀리스의 단순함과 MSA의 유연함을 동시에!
모놀리스(Monolith)의 복잡성과 MSA(Microservices Architecture)의 운영 부담을 해결하기 위해, 모듈 간 경계를 명확히 정의하는 Modular Monolith 아키텍처를 소개
Spring 생태계에서 Modular Monolith 구현을 지원하는 Spring Modulith의 주요 기능(모듈 의존성 검증, 이벤트 기반 통신, 자동 문서화)을 설명
Spring Modulith를 숙박 상품 관리 서비스에 적용한 사례를 통해, 모듈 계층 설계, 헥사고날 아키텍처와의 조합, Bean 이름 충돌 해결 방법 제시
Spring Modulith 도입 시 모듈 경계 설계의 중요성, 공유 모듈 관리, Bean 네이밍 정책 수립 등 고려 사항을 제시하며, 점진적 전환의 장점을 강조
Spring Modulith, Modular Monolith 구현의 핵심
Spring Modulith는 Spring Boot 기반 애플리케이션에서 Modular Monolith 아키텍처를 구현하기 위한 공식 프로젝트이다. 모듈 간 의존성(Module Dependency)을 명시적으로 정의하고, 코드 레벨에서 아키텍처 규칙을 강제한다.
@ApplicationModule 어노테이션: 모듈 정의 및 의존성 허용 범위 설정
allowedDependencies: 모듈 간의 참조 관계(Reference Relationship)를 제한하여 아키텍처 위반을 방지
ApplicationModules.of().verify(): CI 환경에서 모듈 구조 검증 자동화
이러한 기능을 통해, 아키텍처 설계의 일관성을 유지하고, 코드 리뷰의 부담을 줄일 수 있다.
모듈 간 통신: 직접 호출 vs 이벤트
Modular Monolith 환경에서 모듈 간 통신 방식은 트랜잭션 경계에 따라 결정된다. 같은 트랜잭션(Transaction) 내에서 일관성을 유지해야 하는 경우, 직접 호출을 통해 데이터를 주고받는다.
직접 호출: Query Port를 통해 다른 모듈의 데이터를 읽어 비즈니스 로직 수행
이벤트 기반 통신: 트랜잭션 경계를 분리하고 싶을 때 사용, 비동기 처리(Asynchronous Processing)를 통해 성능 향상
Spring Modulith의 @ApplicationModuleListener: @TransactionalEventListener(AFTER_COMMIT) + @Async + @Transactional(REQUIRES_NEW) 조합
이벤트 영속화(Event Persistence)를 통해 장애 발생 시 미완료 이벤트 재처리 가능하며, Scenario API를 통해 이벤트 기반 통신 흐름을 테스트할 수 있다.
자동 문서화 및 모니터링
Spring Modulith는 모듈 구조를 자동으로 문서화하고, 모니터링 기능을 제공하여 개발 생산성을 향상시킨다. 아키텍처 문서(Architecture Documentation)와 코드 간의 괴리를 방지하고, 운영 환경에서의 가시성을 확보한다.
Documenter: 모듈 의존성 다이어그램 자동 생성
Actuator & Observability: OpenTelemetry/Micrometer 기반 모니터링 스택 연동
/actuator/modulith 엔드포인트: 실시간 모듈 구조 확인
자동 문서화는 아키텍처 변경 시 유지보수 비용을 절감하고, 모니터링을 통해 시스템의 이상 징후(Anomaly Detection)를 빠르게 파악할 수 있다.
실제 적용 사례: Stay Product Service
여기어때 상품개발팀은 숙박 상품 관리 서비스에 Spring Modulith를 적용하여, 모듈 간의 의존 관계(Dependency Relationship)를 계층적으로 관리하고 있다. 각 모듈은 헥사고날 아키텍처(Hexagonal Architecture)를 기반으로 설계되어, 외부 시스템과의 결합도를 낮춘다.
계층 구조: 상위 계층은 하위 계층만 참조 가능
헥사고날 아키텍처: 모듈 내부의 도메인 로직과 인프라 레이어 분리
Bean 이름 충돌 해결: 커스텀 BeanNameGenerator를 통해 Bean 이름 충돌 방지
이러한 구조를 통해, 코드의 가독성과 유지보수성을 높이고, 새로운 기능 추가(New Feature) 시 개발 생산성을 향상시킬 수 있었다.
Spring Modulith 도입 시 고려 사항
Spring Modulith를 도입할 때는 모듈 경계 설계, 공유 모듈 관리, Bean 네이밍 정책 등을 신중하게 고려해야 한다. 모듈 분할(Module Decomposition)은 도메인 지식에 기반해야 하며, 변경의 단위와 일치하는 경계를 찾는 것이 중요하다.
모듈 경계 설계: 변경의 빈도와 책임을 고려하여 모듈 분할
공유 모듈 관리: 공유 모듈은 작게 유지하고, 도메인 로직은 지양
Bean 네이밍 정책: Bean 이름 충돌을 방지하기 위한 정책 수립
점진적인 전환을 통해 기존 모놀리스에 Spring Modulith를 적용하고, MSA로의 확장을 고려할 수 있다. 지속적인 개선(Continuous Improvement)을 통해 시스템의 유연성을 확보할 수 있다.