1. 프로젝트 개요
- 목적 : 분산 트랜잭션 기반으로 대회 신청 과정을 안정적·확장 가능하게 처리
- 기술 스택 : Spring Boot 3, Kafka, Redis, PostgreSQL, Docker Compose, Zipkin/Prometheus, JPA + Hibernate
- 아키텍처 스타일 : MSA + Event‑Driven (Kafka), 부분적 CQRS, Redis 기반 Saga State 저장소
2. 현행 구조 분석
| 관점 | 주요 내용 |
|---|
| 레이어드 구조 | Presentation → Application (Service) → Domain → Infrastructure |
| DDD 도입 수준 | Aggregates·도메인 이벤트 개념 존재하나, 로직 대부분이 Service 계층에 집중 |
| 이벤트 흐름 | Kafka competition_saga 토픽에 Application/Payment/Cancellation SagaEvent 발행 후 Consumer 가 공통 처리 |
| 분산 트랜잭션 | 오케스트레이션 기반 Saga 패턴 (SagaState + CompetitionSagaOrchestrator) |
| 데이터 저장소 | RDB (영속 엔티티) + Redis (SagaState 캐시·임시 저장) |
| 사용 패턴 | Repository, DTO, Mapper, Builder, Factory Method, Strategy, State, Saga |
3. 문제점 정리
- 도메인 모델 빈약 : 비즈니스 규칙이 Service 에 집중 → 캡슐화 부족
- 상태 관리 복잡 :
SagaStep ↔ Status 매핑 불명확, 조건문 다중
- 검색 로직 하드코딩 :
ParticipantService.searchParticipants() if/switch 분기 증가 예상
- 이벤트‑핸들러 결합도 : Consumer 에서 비즈니스·보상·알림 로직 혼재
- 예외 처리 일관성 부족 : RuntimeException 섞여있고 ServiceCode 매핑 누락
- 테스트 부재 : 도메인·이벤트 흐름에 대한 단위/통합 테스트 부족
4. 리팩토링 목표
| 구분 | 목표 |
|---|
| 도메인 강화 | 규칙을 Aggregate 내부로 이전, 풍부한 도메인 모델 구현 |
| 패턴 정제 | 상태 패턴·전략 패턴·템플릿 메서드 패턴 적용으로 조건문 제거 & 확장성 확보 |
| CQRS 명확화 | Command/Query 서비스 완전 분리, 읽기 모델 최적화 |
| 이벤트 모듈화 | 도메인 이벤트 → Spring @EventListener, Consumer 는 오케스트레이션 전담 |
| 예외 계층화 | DomainException → ServiceException → GlobalHandler 로 일관 처리 |
| 테스트 확보 | 단위(도메인)·슬라이스(Repository)·통합(Saga flow) 테스트 추가 |
5. 주요 적용 설계
5.1 도메인 모델 (예)
public class Competition extends BaseEntity {
public void apply(Participant p){
if(isDuplicate(p)) throw new CompetitionException(ALREADY_APPLIED);
if(isFull()) throw new CompetitionException(CAPACITY_FULL);
mappings.add(CompetitionParticipantMapping.create(this,p));
DomainEvents.publish(new ParticipantAppliedEvent(id,p.getId()));
}
}
5.2 상태 패턴
ParticipantState 인터페이스 → AppliedState, PaidState, SelectedState, CancelledState 구현
CompetitionParticipantMapping 가 현재 State 객체 보유, 행동 위임
5.3 전략 패턴 (검색)
interface SearchStrategy { Page<?> search(String keyword, Pageable p); }
- 구현체
TitleSearchStrategy, StatusSearchStrategy 등
SearchStrategyFactory 가 DI‑container 로부터 Map 주입받아 선택
5.4 템플릿 메서드 기반 Saga
AbstractSaga<T extends SagaEvent> 에 공통 단계 정의
- ApplicationSaga / PaymentSaga 등이 세부 행동 오버라이드
5.5 예외 계층
DomainException(code, message) ← 모든 도메인 예외 상속
GlobalExceptionHandler 가 code → HTTP Status 매핑 일원화
6. 기대 효과
- 응집도↑, 결합도↓ : 도메인 규칙 집중, Service 간섭 감소
- 확장 용이 : 신규 상태·검색 타입 등 OCP 준수로 코드 수정 최소화
- 가독성 & 테스트성 : 상태 전이·이벤트 흐름 명료, 단위 테스트 작성 쉬움
- 신뢰성 : 일관된 예외 처리 + 멱등성 보장으로 오류·중복 처리 최소화
- 운영 편의 : 로그 상세화, 보상 로직 분리 ⇒ 장애 분석 속도 개선
7. 단계별 작업 목록 (Roadmap)
| Phase | 작업 | 산출물 |
|---|
| 1 | 도메인 규칙 이관 / Aggregate 리팩터링 | Updated entity & event classes |
| 2 | 상태 패턴 도입 (Participant) | state 패키지 + 테스트 |
| 3 | 검색 전략 분리 | strategy 패키지, Factory |
| 4 | CQRS 서비스 분리 | CommandService / QueryService |
| 5 | AbstractSaga 생성 + 하위 Saga 마이그레이션 | saga 패키지 재구성 |
| 6 | 예외 계층 & GlobalHandler 정비 | error 패키지, 공통 ApiResponse |
| 7 | 테스트 작성 | JUnit + Spring Boot Test 보고서 |
| 8 | 문서화 & 코드 리뷰 | ADR, UML (PlantUML) |