이 프로젝트는 초기 개발 생산성과 장기적인 확장성(MSA 전환)을 동시에 고려하여
다음과 같은 아키텍처 결정을 기반으로 설계되었습니다.
Modular Monolith + DDD Layered Architecture + CQRS + Event-Driven
(Hexagonal Architecture의 일부 원칙을 선택적으로 차용)
- 초기에는 모놀리식으로 빠르게 개발
- 도메인 간 결합을 최소화하여 나중에 MSA로 분리 가능
- 복잡한 조회 요구사항을 도메인 모델에서 분리
- “왜 이런 구조를 선택했는지”를 명확히 설명할 수 있는 설계
프로젝트는 도메인 단위로 명확히 분리된 모듈형 모놀리식 구조를 사용합니다.
post
social
user
-
각 모듈은 미래의 마이크로서비스 경계를 의미합니다.
-
현재는 단일 애플리케이션이지만,
향후 서비스 분리 시 모듈 단위로 이동만 하면 되도록 설계되었습니다.
각 도메인 모듈 내부는 다음과 같은 전통적인 레이어드 구조를 따릅니다.
presentation
application
domain
infrastructure
- presentation
- HTTP API
- Request / Response DTO
- 인증·인가 처리
- application
- 유스케이스 실행
- 트랜잭션 경계
- 도메인 객체 조합
- domain
- 비즈니스 규칙의 중심
- Aggregate Root / Entity
- infrastructure
- JPA, QueryDSL, 메시징 등 기술 구현
도메인 모델은 Aggregate 단위로 명확히 경계를 설정했습니다.
예시 (Post 도메인):
Post: Aggregate RootPostPlace: Post 내부 Entity- Place는 Post 없이 단독으로 존재할 수 없음
Post
└─ PostPlace (내부 Entity)
- Aggregate 외부 객체는 ID로만 참조
- Aggregate 내부만 객체 연관관계 허용
- 비즈니스 규칙은 Entity 내부에 위치
- 조회 요구사항은 Domain에 포함하지 않음
조회 요구사항이 복잡해짐에 따라 쓰기(Command)와 읽기(Query)를 분리했습니다.
- 도메인 규칙과 정합성에 집중
- Aggregate 기반 처리
- 트랜잭션 경계 명확
- DTO / Projection 기반 조회
- QueryDSL / Native SQL 사용
- 화면 요구사항에 맞춘 자유로운 조회
application.command → 도메인 규칙
application.query → 조회 최적화
이를 통해 도메인 모델이 조회 요구사항에 오염되지 않도록 했습니다.
도메인 간 직접 의존을 제거하기 위해 이벤트 기반 통신을 사용합니다.
- 도메인 간 직접 호출 ❌
- 모든 상호작용은 이벤트를 통해 전달
- 이벤트는 트랜잭션 커밋 이후(AFTER_COMMIT) 발행
Post 생성
→ PostCreatedEvent 발행
→ 다른 도메인에서 이벤트 수신 후 처리
이 구조는 향후 Kafka, SQS 등 메시지 브로커로 자연스럽게 확장할 수 있습니다.
이 프로젝트는 순수한 Hexagonal Architecture는 사용하지 않습니다.
대신, 교체 가능성이 높은 지점에만 Port 개념을 선택적으로 도입했습니다.
PostRepository(저장소)EventPublisher(이벤트 발행)
application
└─ port
└─ out
└─ PostRepository
- QueryRepository (조회 전용)
- 단순 유틸 / 헬퍼
과도한 추상화를 피하면서도 MSA 전환 시 수정 비용을 최소화하기 위해서입니다.