Entry5.0을 마치고 진행한 회고에서 애플리케이션의 문제보다는 UX적인 문제점이 많이 지적되었습니다. 실제로 올해 서비스는 99.849%의 성공률을 기록하며 성공적으로 마무리되었으며, 발생했던 치명적인 오류는 없었습니다.
1기 선배님들부터 5년동안 시스템을 구축하며 생긴 노하우를 바탕으로 잘 돌아가는 어플리케이션 구축에는 무리가 없지만 유지보수에는 어려움이 있다는 문제가 있었습니다. 따라서 저희는 유연성
과 재사용성
을 최우선으로 고려하여 유지보수
가 가능한 코드를 작성하고자 하였습니다. 궁극적으로 견고한 모노리스 어플리케이션을 구축하여 추후 더 다양한 시도를 할 수 있도록 하는 기반을 다지고자 하였습니다.
유연하고 재사용성 높은 코드를 작성하기 위해 해결 방안을 탐색하던 중 우아한 테크세미나에서 발표된 <우아한 모노리스>의 모듈형 모노리스
적용 사례를 접하게 되었습니다. 각 도메인을 모듈로 분리하여 하나의 서비스로 동작하도록 하며 분리된 서비스들을 조립하여 애플리케이션을 구성한다면 문제를 해결할 수 있을 것이라고 판단하였습니다.
완성도 있는 모듈형 모노리스 애플리케이션을 구성하기 위해서 우선 <우아한 모노리스>의 사례를 분석했습니다. <우아한 모노리스>에서는 다음과 같은 전략들을 사용하여 애플리케이션을 분해하였습니다.
- 도메인 지향적 멀티모듈 프로젝트 구성
- 공통적인 관심사는 common 모듈에서 관리
- 구현 세부사항은 private로 외부 접근 방지
- 외부 모듈과는 인터페이스로만 교류
- 의도하지 않은 빈의 공유 방지를 위해 모듈별로 컨텍스트 분리
- 분리된 컨텍스트들은 하나의 루트 컨텍스트를 부모로 가짐
- 의도적으로 공유하고자 하는 인터페이스 빈은
@Published
어노테이션을 통해 루트 컨텍스트로 등록하여 간접적으로 공유
분해한 서비스는 하나의 모듈에서 결합되어 사용되므로 서비스간의 결합도를 최소화함에 따른 개발상의 이점과 동시에 서비스의 자유로운 분리/결합으로 인한 운영상의 이점을 모두 취할 수 있었습니다.
<우아한 모노리스>의 사례를 바탕으로 애플리케이션을 구성하기로 하였습니다. 기존 전략은 그대로 가져가도록 하되, 웹 어플리케이션에 맞게 몇 가지 전략을 추가하였습니다.
-
루트 컨텍스트에 등록된 빈(
@Published
어노테이션이 붙은 빈) 중@RestController
어노테이션이 붙은 빈은 SERVLET 컨텍스트로 복사하도록 하여 SERVLET 컨텍스트에서 Controller에 라우팅이 가능하도록 하였습니다. -
컨텍스트 분리와 재결합에 따른 빈 순환 참조 문제를 해결하기 위하여 도메인의 의존 방향을 정의하였으며, 빈 초기화 순서에 따른 문제를 해결하기 위하여 모듈에 우선순위를 부여하여 참조되는 컨텍스트의 빈을 먼저 초기화 하도록 하였습니다.
우선 도메인 지향적 모듈화를 위해 기획에서 도메인을 도출하고, 각 도메인마다 하나의 모듈을 구축하여 최종적으로 5개의 모듈의 서비스가 상호작용하며 Entry6.0을 구성합니다.
- 지원자 : 입학전형 시스템의 주체이며, 지원 서비스를 통해 원서를 접수합니다.
- 관리자 : 접수된 원서를 확인하고 상태 서비스를 통해 지원자를 관리합니다.
- 지원 : 지원자의 원서 정보를 관리합니다.
- 성적 : 지원자의 성적 정보를 관리합니다.
- 알람 : 지원자의 상태에 따라 지원자에게 알림을 전송합니다.
또한 분리된 애플리케이션을 조립하기 위해서 메인 모듈을, 인증이나 로깅과 같은 수직적 관심사를 공통적으로 구축하기 위해서 공통 모듈을 각각 구축하여 서비스간의 결합이 원활하게 이루어질 수 있도록 하였습니다.
<domain>
ㄴ src
ㄴ main
ㄴ java
ㄴ kr.hs.entrydsm.<domain>
ㄴ presenter
ㄴ web
ㄴ usecase
ㄴ dto
ㄴ exception
ㄴ domain
ㄴ entity
ㄴ repository
ㄴ infrastructure
ㄴ database
ㄴ integrate
ㄴ <other domains>
<domain>ModuleConfiguration.java
Enable<domain>Module.java
ㄴ resources
ㄴ test
- presenter 계층은 애플리케이션의 최외곽 계층으로, 외부와의 상호작용을 담당합니다. 주로
@RestController
어노테이션이 붙은 클래스가 위치합니다. - usecase 계층에서는 애플리케이션의 핵심 비즈니스 로직이 위치합니다. 애플리케이션의
@Service
어노테이션이 붙은 클래스와, 해당 클래스의 인터페이스 및 DTO, 로직 처리 중 발생할 수 있는 exception이 위치합니다. - domain 계층에서는 도메인의 속성과 행위를 정의합니다.
@Entity
어노테이션이 붙은 클래스와@Repository
어노테이션이 붙은 클래스의 인터페이스가 위치합니다. - infrastructure 계층에서는 서비스가 운영되는 인프라에 대한 부분을 구성합니다. 현재는 DB 관련 로직만 위치하고 있습니다.
- integrate 계층에서는 모듈과의 상호작용을 위한 로직을 구성합니다. 다른 컨텍스트와의 공유를 필요로 하는 domain이 위치합니다.
- <domain>ModuleConfiguration 클래스에서는 모듈의 설정사항을 구성하고 컨텍스트에 빈을 등록하는 역할을 수행합니다. 공통 모듈의 ModuleConfiguration 추상 클래스를 상속받아 구현합니다.
- Enable<domain>Module 어노테이션에서는 <domain>ModuleConfiguration 클래스를 Import하여 어노테이션화합니다.
main
ㄴ src
ㄴ main
ㄴ java
ㄴ kr.hs.entrydsm.main
ㄴ configuration
ㄴ integration
ㄴ <domain>
ㄴ <domain>Integrate<another domain>Service.java
.
.
.
ㄴ MainApplication
ㄴ resources
ㄴ test
- configuration 패키지에서는 메인에서 다른 모듈들을 통합할 때에 고려해야 할 전략들을 구현합니다.
- integration 패키지에서는 의존하는 모듈에서 의존받는 모듈로의 통합을 구현합니다.
common
ㄴ src
ㄴ main
ㄴ java
ㄴ kr.hs.entrydsm.common
ㄴ context
ㄴ beans
ㄴ Published.java
ㄴ PublishedComponentRegisteringPostProcessor.java
ㄴ ModuleConfiguration.java
ㄴ Module.java
ㄴ resources
ㄴ test
- PublishedComponentRegisteringPostProcessor 클래스에서는
@Published
어노테이션이 붙은 컴포넌트를 루트 컨텍스트로 등록시키는 로직을 구현합니다. <우아한 모노리스>에서 공개된 코드를 바탕으로 최적화하여 구현하였습니다. - ModuleConfiguration 클래스는 서비스 모듈 내에 있는 설정사항 파일의 추상 클래스입니다.
EntryDSM
은 대덕소프트웨어마이스터고등학교 입학전형 시스템 개발팀으로, 학교 특성상 매 해마다 진행되는 서류 원서 접수와 면접을 통한 신입생 선발 프로세스를 개선하고자 하는 목표를 가지고 다양한 기술을 탐구하며 요구사항과 팀의 상황에 알맞게 해당 기술을 도입하여 매 해마다 Entry series
를 개발합니다.
Entry
는 EntryDSM에서 매 해마다 신입생 원서 접수와 면접을 위해서 개발하는 입학전형 시스템 프로젝트입니다. 원서 접수 기간동안 수 만 건의 요청을 안전하게 처리하는 것을 최우선으로 프로젝트가 진행됩니다.