설계
목적
-
Clean Architecture + DDD 패턴
핵심 원칙
- 의존성 방향: 항상 외부(infrastructure) → 내부(domain) 방향
- 단일 책임: 각 컴포넌트는 하나의 명확한 책임만
- 레이어 분리: 각 레이어는 자신보다 안쪽 레이어만 의존
최종 설계 구조
1. 레이어 구조
┌─────────────────────────────────────┐
│ Presentation Layer (Controller) │ ← 사용자 인터페이스
├─────────────────────────────────────┤
│ Application Layer (Service) │ ← 유스케이스 조율
├─────────────────────────────────────┤
│ Domain Layer (Entity, Service) │ ← 비즈니스 로직 핵심
├─────────────────────────────────────┤
│ Infrastructure Layer (Repo, API) │ ← 기술적 세부사항
└─────────────────────────────────────┘
의존성 방향:
Controller → Application Service → Domain Service → Entity
↓ ↓
Infrastructure ←──────────────┘
(Repository, Sender)
서비스 기능 추가 시 설계 고민
예약 시간 이후에 추가된 유저도 전송 대상으로 포함 되도록 수정
🏗️ 아키텍처 레이어 구조
graph TD
subgraph Presentation["🎯 Presentation Layer (Scheduling Trigger)"]
A[MessageUtils.registerTaskSchedule<br/>- 예약 시간 30분 전에 TaskScheduler 등록]
end
subgraph Application1["📤 Application Layer (Sender)"]
B[KakaoAlimtalkSenderImpl<br/>sendKakaoMessageByReservationByMessageIds]
end
subgraph Application2["📋 Application Layer (Use Case)"]
C[MessageLogDetailServiceImpl<br/>addMissingDetailsBeforeSend]
C1["1 - MessageLog 조회 (그룹 정보 포함)"]
C2["2 - 타겟 그룹 조합 (selectGroup + includeGroups)"]
C3["3 - 현재 시점 유저 조회"]
C4["4 - 제외 유저 필터링"]
C5["5 - 누락 유저 추출"]
C6["6 - Domain Service 호출"]
C --> C1 --> C2 --> C3 --> C4 --> C5 --> C6
end
subgraph Domain["🏛️ Domain Layer"]
D[MessageLogDetailDomainService<br/>createDetailsForUsers]
D1["1 - 도메인 검증 (canAddNewDetails)"]
D2["2 - MessageContent 레벨별 조회"]
D3["3 - User 레벨 매칭"]
D4["4 - MessageLogDetail 생성"]
D --> D1 --> D2 --> D3 --> D4
end
A -->|schedules<br/>30min before| B
B -->|calls| C
C6 -->|calls| D
D -->|추가 된 유저 반환| B
style Presentation fill:#e1f5ff
style Application1 fill:#fff4e1
style Application2 fill:#fff4e1
style Domain fill:#f0e1ff
계층별로 역할
Presentation
- 역할
- 스케줄링 트리거
- 장점
- 실행 시점만 관리
- 비즈니스 로직과 무관
Application Sender
- 역할
- 메시지 전송 서비스 레이어
- 장점
- 플랫폼별 전송 로직 캡슐화
- 여러 API 통신 관리
Application Service
- 역할
- 누락 유저 추가 Detail 서비스 레이어
- 장점
- 여러 Repository 조합
- 다른 UseCase에서 사용할 수 있음
Domain
- 역할
- 순수 비즈니스 로직
- 장점
- DB와 같은 외부 의존성이 없음
- 단위 유닛 테스트 가능