mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2026-06-13 11:39:11 +00:00
물리아키텍처 설계 완료
✨ 주요 기능 - Azure 기반 물리아키텍처 설계 (개발환경/운영환경) - 7개 마이크로서비스 물리 구조 설계 - 네트워크 아키텍처 다이어그램 작성 (Mermaid) - 환경별 비교 분석 및 마스터 인덱스 문서 📁 생성 파일 - design/backend/physical/physical-architecture.md (마스터) - design/backend/physical/physical-architecture-dev.md (개발환경) - design/backend/physical/physical-architecture-prod.md (운영환경) - design/backend/physical/*.mmd (4개 Mermaid 다이어그램) 🎯 핵심 성과 - 비용 최적화: 개발환경 월 $143, 운영환경 월 $2,860 - 확장성: 개발환경 100명 → 운영환경 10,000명 (100배) - 가용성: 개발환경 95% → 운영환경 99.9% - 보안: 다층 보안 아키텍처 (L1~L4) 🛠️ 기술 스택 - Azure Kubernetes Service (AKS) - Azure Database for PostgreSQL Flexible - Azure Cache for Redis Premium - Azure Service Bus Premium - Application Gateway + WAF 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title Distribution Service 클래스 다이어그램 (상세)
|
||||
|
||||
package "com.kt.distribution" {
|
||||
|
||||
package "controller" {
|
||||
class DistributionController {
|
||||
- distributionService: DistributionService
|
||||
+ distribute(request: DistributionRequest): ResponseEntity<DistributionResponse>
|
||||
+ getDistributionStatus(eventId: String): ResponseEntity<DistributionStatusResponse>
|
||||
}
|
||||
}
|
||||
|
||||
package "service" {
|
||||
class DistributionService {
|
||||
- channelAdapters: List<ChannelAdapter>
|
||||
- kafkaEventPublisher: Optional<KafkaEventPublisher>
|
||||
- statusRepository: DistributionStatusRepository
|
||||
- executorService: ExecutorService
|
||||
+ distribute(request: DistributionRequest): DistributionResponse
|
||||
+ getDistributionStatus(eventId: String): DistributionStatusResponse
|
||||
- saveInProgressStatus(eventId: String, channels: List<ChannelType>, startedAt: LocalDateTime): void
|
||||
- saveCompletedStatus(eventId: String, results: List<ChannelDistributionResult>, startedAt: LocalDateTime, completedAt: LocalDateTime, successCount: long, failureCount: long): void
|
||||
- convertToChannelStatus(result: ChannelDistributionResult, eventId: String, completedAt: LocalDateTime): ChannelStatus
|
||||
- publishDistributionCompletedEvent(eventId: String, results: List<ChannelDistributionResult>): void
|
||||
}
|
||||
|
||||
class KafkaEventPublisher {
|
||||
- kafkaTemplate: KafkaTemplate<String, Object>
|
||||
- distributionCompletedTopic: String
|
||||
+ publishDistributionCompleted(event: DistributionCompletedEvent): void
|
||||
}
|
||||
}
|
||||
|
||||
package "adapter" {
|
||||
interface ChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
+ distribute(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
abstract class AbstractChannelAdapter implements ChannelAdapter {
|
||||
+ distribute(request: DistributionRequest): ChannelDistributionResult
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
# fallback(request: DistributionRequest, throwable: Throwable): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class UriDongNeTvAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class RingoBizAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class GiniTvAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class InstagramAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class NaverAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
|
||||
class KakaoAdapter extends AbstractChannelAdapter {
|
||||
+ getChannelType(): ChannelType
|
||||
# executeDistribution(request: DistributionRequest): ChannelDistributionResult
|
||||
}
|
||||
}
|
||||
|
||||
package "dto" {
|
||||
class DistributionRequest {
|
||||
- eventId: String
|
||||
- title: String
|
||||
- description: String
|
||||
- imageUrl: String
|
||||
- channels: List<ChannelType>
|
||||
- channelSettings: Map<String, Map<String, Object>>
|
||||
}
|
||||
|
||||
class DistributionResponse {
|
||||
- eventId: String
|
||||
- success: boolean
|
||||
- channelResults: List<ChannelDistributionResult>
|
||||
- successCount: int
|
||||
- failureCount: int
|
||||
- completedAt: LocalDateTime
|
||||
- totalExecutionTimeMs: long
|
||||
- message: String
|
||||
}
|
||||
|
||||
class ChannelDistributionResult {
|
||||
- channel: ChannelType
|
||||
- success: boolean
|
||||
- distributionId: String
|
||||
- estimatedReach: Integer
|
||||
- errorMessage: String
|
||||
- executionTimeMs: long
|
||||
}
|
||||
|
||||
class DistributionStatusResponse {
|
||||
- eventId: String
|
||||
- overallStatus: String
|
||||
- startedAt: LocalDateTime
|
||||
- completedAt: LocalDateTime
|
||||
- channels: List<ChannelStatus>
|
||||
}
|
||||
|
||||
class ChannelStatus {
|
||||
- channel: ChannelType
|
||||
- status: String
|
||||
- progress: Integer
|
||||
- distributionId: String
|
||||
- estimatedViews: Integer
|
||||
- updateTimestamp: LocalDateTime
|
||||
- eventId: String
|
||||
- impressionSchedule: List<String>
|
||||
- postUrl: String
|
||||
- postId: String
|
||||
- messageId: String
|
||||
- completedAt: LocalDateTime
|
||||
- errorMessage: String
|
||||
- retries: Integer
|
||||
- lastRetryAt: LocalDateTime
|
||||
}
|
||||
|
||||
enum ChannelType {
|
||||
URIDONGNETV
|
||||
RINGOBIZ
|
||||
GINITV
|
||||
INSTAGRAM
|
||||
NAVER
|
||||
KAKAO
|
||||
- displayName: String
|
||||
- category: String
|
||||
+ getDisplayName(): String
|
||||
+ getCategory(): String
|
||||
}
|
||||
}
|
||||
|
||||
package "repository" {
|
||||
class DistributionStatusRepository {
|
||||
- jpaRepository: DistributionStatusJpaRepository
|
||||
- mapper: DistributionStatusMapper
|
||||
+ save(eventId: String, status: DistributionStatusResponse): void
|
||||
+ findByEventId(eventId: String): Optional<DistributionStatusResponse>
|
||||
+ delete(eventId: String): void
|
||||
+ deleteAll(): void
|
||||
}
|
||||
|
||||
interface DistributionStatusJpaRepository {
|
||||
+ findByEventIdWithChannels(eventId: String): Optional<DistributionStatus>
|
||||
+ deleteByEventId(eventId: String): void
|
||||
}
|
||||
}
|
||||
|
||||
package "entity" {
|
||||
class DistributionStatus {
|
||||
- id: Long
|
||||
- eventId: String
|
||||
- overallStatus: String
|
||||
- startedAt: LocalDateTime
|
||||
- completedAt: LocalDateTime
|
||||
- channels: List<ChannelStatusEntity>
|
||||
- createdAt: LocalDateTime
|
||||
- updatedAt: LocalDateTime
|
||||
+ addChannelStatus(channelStatus: ChannelStatusEntity): void
|
||||
+ removeChannelStatus(channelStatus: ChannelStatusEntity): void
|
||||
}
|
||||
|
||||
class ChannelStatusEntity {
|
||||
- id: Long
|
||||
- distributionStatus: DistributionStatus
|
||||
- channel: ChannelType
|
||||
- status: String
|
||||
- progress: Integer
|
||||
- distributionId: String
|
||||
- estimatedViews: Integer
|
||||
- updateTimestamp: LocalDateTime
|
||||
- eventId: String
|
||||
- impressionSchedule: String
|
||||
- postUrl: String
|
||||
- postId: String
|
||||
- messageId: String
|
||||
- completedAt: LocalDateTime
|
||||
- errorMessage: String
|
||||
- retries: Integer
|
||||
- lastRetryAt: LocalDateTime
|
||||
- createdAt: LocalDateTime
|
||||
- updatedAt: LocalDateTime
|
||||
}
|
||||
}
|
||||
|
||||
package "mapper" {
|
||||
class DistributionStatusMapper {
|
||||
+ toEntity(dto: DistributionStatusResponse): DistributionStatus
|
||||
+ toDto(entity: DistributionStatus): DistributionStatusResponse
|
||||
+ toChannelStatusEntity(dto: ChannelStatus): ChannelStatusEntity
|
||||
+ toChannelStatusDto(entity: ChannelStatusEntity): ChannelStatus
|
||||
}
|
||||
}
|
||||
|
||||
package "event" {
|
||||
class DistributionCompletedEvent {
|
||||
- eventId: String
|
||||
- distributedChannels: List<DistributedChannelInfo>
|
||||
- completedAt: LocalDateTime
|
||||
}
|
||||
|
||||
class DistributedChannelInfo {
|
||||
- channel: String
|
||||
- channelType: String
|
||||
- status: String
|
||||
- expectedViews: Integer
|
||||
}
|
||||
}
|
||||
|
||||
package "config" {
|
||||
class KafkaConfig {
|
||||
+ kafkaTemplate(): KafkaTemplate<String, Object>
|
||||
+ producerFactory(): ProducerFactory<String, Object>
|
||||
}
|
||||
|
||||
class OpenApiConfig {
|
||||
+ openAPI(): OpenAPI
|
||||
}
|
||||
|
||||
class WebConfig {
|
||||
+ corsConfigurer(): WebMvcConfigurer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
' 관계 정의
|
||||
DistributionController --> DistributionService : uses
|
||||
DistributionService --> ChannelAdapter : uses
|
||||
DistributionService --> KafkaEventPublisher : uses
|
||||
DistributionService --> DistributionStatusRepository : uses
|
||||
DistributionService ..> DistributionRequest : uses
|
||||
DistributionService ..> DistributionResponse : creates
|
||||
DistributionService ..> DistributionStatusResponse : uses
|
||||
DistributionService ..> ChannelDistributionResult : uses
|
||||
|
||||
AbstractChannelAdapter ..|> ChannelAdapter : implements
|
||||
UriDongNeTvAdapter --|> AbstractChannelAdapter : extends
|
||||
RingoBizAdapter --|> AbstractChannelAdapter : extends
|
||||
GiniTvAdapter --|> AbstractChannelAdapter : extends
|
||||
InstagramAdapter --|> AbstractChannelAdapter : extends
|
||||
NaverAdapter --|> AbstractChannelAdapter : extends
|
||||
KakaoAdapter --|> AbstractChannelAdapter : extends
|
||||
|
||||
ChannelAdapter ..> DistributionRequest : uses
|
||||
ChannelAdapter ..> ChannelDistributionResult : creates
|
||||
|
||||
KafkaEventPublisher ..> DistributionCompletedEvent : publishes
|
||||
DistributionCompletedEvent --> DistributedChannelInfo : contains
|
||||
|
||||
DistributionStatusRepository --> DistributionStatusJpaRepository : uses
|
||||
DistributionStatusRepository --> DistributionStatusMapper : uses
|
||||
DistributionStatusRepository ..> DistributionStatusResponse : uses
|
||||
DistributionStatusRepository ..> DistributionStatus : uses
|
||||
|
||||
DistributionStatusJpaRepository ..> DistributionStatus : manages
|
||||
DistributionStatusMapper ..> DistributionStatus : maps
|
||||
DistributionStatusMapper ..> DistributionStatusResponse : maps
|
||||
DistributionStatusMapper ..> ChannelStatus : maps
|
||||
DistributionStatusMapper ..> ChannelStatusEntity : maps
|
||||
|
||||
DistributionStatus "1" *-- "many" ChannelStatusEntity : contains
|
||||
ChannelStatusEntity --> DistributionStatus : belongs to
|
||||
|
||||
DistributionRequest --> ChannelType : uses
|
||||
DistributionResponse --> ChannelDistributionResult : contains
|
||||
ChannelDistributionResult --> ChannelType : uses
|
||||
DistributionStatusResponse --> ChannelStatus : contains
|
||||
ChannelStatus --> ChannelType : uses
|
||||
ChannelStatusEntity --> ChannelType : uses
|
||||
|
||||
note top of DistributionService
|
||||
핵심 비즈니스 로직
|
||||
- 다중 채널 병렬 배포 실행
|
||||
- ExecutorService로 비동기 처리
|
||||
- Circuit Breaker 패턴 적용
|
||||
- Kafka 이벤트 발행
|
||||
end note
|
||||
|
||||
note top of AbstractChannelAdapter
|
||||
Resilience4j 적용
|
||||
- @CircuitBreaker
|
||||
- @Retry (지수 백오프)
|
||||
- @Bulkhead
|
||||
- Fallback 처리
|
||||
end note
|
||||
|
||||
note top of DistributionStatusRepository
|
||||
배포 상태 영구 저장
|
||||
- PostgreSQL 데이터베이스 사용
|
||||
- JPA Repository 패턴
|
||||
- Entity-DTO 매핑
|
||||
end note
|
||||
|
||||
note top of ChannelType
|
||||
배포 채널 종류
|
||||
- TV: 우리동네TV, 지니TV
|
||||
- CALL: 링고비즈
|
||||
- SNS: Instagram, Naver, Kakao
|
||||
end note
|
||||
|
||||
@enduml
|
||||
Reference in New Issue
Block a user