스케줄 도메인의 조회 서비스 (이벤트 프로젝션 기반 일정 조회)
- 포트: 8080 (기본값)
- 데이터베이스: MongoDB (localhost:27017/schedule_query)
- Kafka: localhost:9092
- Consumer Group: schedule-query-service
CQRS Query Side로서 Command Service로부터 이벤트를 수신하여 MongoDB에 프로젝션
ScheduleCreated - Command Service로부터 수신
토픽: schedule.events
Record 클래스:
// 📁 event/schema/ScheduleCreated.java
public record ScheduleCreated(
UUID scheduleId,
UUID ownerId,
String title,
String description,
LocalDateTime startAt,
LocalDateTime endAt,
String source,
UUID studyGroupId, // STUDY source용 필드 (nullable)
UUID meetingId, // STUDY source용 필드 (nullable)
UUID datingMeetingUuid, // DATING source용 필드 (nullable)
long version,
LocalDateTime occurredAt
) {}예시 JSON:
{
"scheduleId": "8f8e5d5e-63d7-4a4e-9d2f-1b93b9a1e0c7",
"ownerId": "4c9d3e20-2fda-4ab0-8631-0c2f9b8c2d11",
"title": "스터디 모임",
"description": "자료 준비",
"startAt": "2025-10-01T10:00:00",
"endAt": "2025-10-01T12:00:00",
"source": "CUSTOM",
"version": 1,
"occurredAt": "2025-09-24T15:15:30"
}ScheduleUpdated - Command Service로부터 수신
토픽: schedule.events
Record 클래스:
// 📁 event/schema/ScheduleUpdated.java
public record ScheduleUpdated(
UUID scheduleId,
UUID ownerId,
String title,
String description,
LocalDateTime startAt,
LocalDateTime endAt,
String source,
UUID studyGroupId, // STUDY source용 필드 (nullable)
UUID meetingId, // STUDY source용 필드 (nullable)
UUID datingMeetingUuid, // DATING source용 필드 (nullable)
long version,
LocalDateTime occurredAt
) {}예시 JSON은 ScheduleCreated와 동일 구조입니다.
ScheduleDeleted - Command Service로부터 수신
토픽: schedule.events
Record 클래스:
// 📁 event/schema/ScheduleDeleted.java
public record ScheduleDeleted(
UUID scheduleId,
UUID ownerId,
String source,
long version,
LocalDateTime occurredAt
) {}예시 JSON:
{
"scheduleId": "8f8e5d5e-63d7-4a4e-9d2f-1b93b9a1e0c7",
"ownerId": "4c9d3e20-2fda-4ab0-8631-0c2f9b8c2d11",
"source": "CUSTOM",
"version": 3,
"occurredAt": "2025-09-24T16:00:00"
}- ScheduleCreated: MongoDB에 새로운 일정 문서 생성
- ScheduleUpdated: 기존 일정 문서 업데이트 (버전 체크)
- ScheduleDeleted: 일정 문서 삭제 (버전 체크)
- 이벤트 버전 관리로 순서 보장 및 멱등성 확보
./gradlew bootRun-
GET
/schedules/{scheduleId}— 일정 단건 조회- Header:
X-Owner-Id: <UUID> - Response:
- 200 OK: 일정 정보 반환
- 404 NOT FOUND: "스케쥴이 없습니다"
- Header:
-
GET
/schedules— 특정 날짜의 일정 목록 조회 (페이징)- Header:
X-Owner-Id: <UUID> - Query Param:
day(required) - LocalDate (예: 2025-10-01) - Pagination:
page,size,sort(기본 정렬: startAt) - Response: Page
- Header:
-
GET
/schedules/all— 사용자의 전체 일정 목록 조회 (페이징)- Header:
X-Owner-Id: <UUID> - Pagination:
page,size,sort(기본 정렬: startAt) - Response: Page
- Header:
{
"id": "8f8e5d5e-63d7-4a4e-9d2f-1b93b9a1e0c7",
"ownerId": "4c9d3e20-2fda-4ab0-8631-0c2f9b8c2d11",
"title": "스터디 모임",
"description": "자료 준비",
"startAt": "2025-10-01T10:00:00",
"endAt": "2025-10-01T12:00:00",
"source": "CUSTOM",
"eventVersion": 1,
"occurredAt": "2025-09-24T15:15:30",
"updatedAt": "2025-09-24T15:15:30"
}- CQRS Query Side (읽기 전용)
- Event-Driven Architecture (Kafka 기반 이벤트 수신)
- Event Sourcing Projection (이벤트를 MongoDB로 프로젝션)
- MongoDB (조회 성능 최적화를 위한 Document DB)
- Query Side는 데이터 수정 불가 (읽기 전용)
- 모든 데이터 변경은 Command Service의 이벤트를 통해서만 반영
- 이벤트 버전 관리로 중복 처리 방지 및 순서 보장
- MongoDB 인덱스:
ownerId + startAt복합 인덱스로 조회 성능 최적화
- ScheduleDocument
- 식별자: id (scheduleId, UUID), ownerId (UUID)
- 내용: title, description (nullable), startAt, endAt
- 분류: source (CUSTOM/DATING/STUDY)
- STUDY 필드: studyGroupId (nullable), meetingId (nullable)
- DATING 필드: datingMeetingUuid (nullable)
- 이벤트 추적: eventVersion, occurredAt, updatedAt
- 인덱스:
ownerId_startAt_idx(복합 인덱스)
- Java 21, Spring Boot 3.5.6
- Spring Web, Spring Data MongoDB, Spring Kafka
- Lombok (제한: @Getter, @RequiredArgsConstructor)
- Gradle
- Command Service와의 통신은 Kafka 이벤트 기반으로만 수행
- 조회 성능을 위해 MongoDB의 Document 모델 활용
- 페이징 및 정렬 기능 제공으로 대용량 데이터 처리 지원