mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 16:46:23 +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>
228 lines
7.7 KiB
Plaintext
228 lines
7.7 KiB
Plaintext
@startuml
|
|
!theme mono
|
|
|
|
title Content Service - 클래스 다이어그램 요약 (Clean Architecture)
|
|
|
|
' ============================================
|
|
' 레이어 구조 표시
|
|
' ============================================
|
|
package "Domain Layer" <<Rectangle>> {
|
|
class Content {
|
|
- id: Long
|
|
- eventId: String
|
|
- images: List<GeneratedImage>
|
|
+ addImage(image: GeneratedImage): void
|
|
+ getSelectedImages(): List<GeneratedImage>
|
|
}
|
|
|
|
class GeneratedImage {
|
|
- id: Long
|
|
- eventId: String
|
|
- style: ImageStyle
|
|
- platform: Platform
|
|
- cdnUrl: String
|
|
+ select(): void
|
|
+ deselect(): void
|
|
}
|
|
|
|
class Job {
|
|
- id: String
|
|
- eventId: String
|
|
- status: Status
|
|
- progress: int
|
|
+ start(): void
|
|
+ updateProgress(progress: int): void
|
|
+ complete(resultMessage: String): void
|
|
+ fail(errorMessage: String): void
|
|
}
|
|
|
|
enum ImageStyle {
|
|
FANCY
|
|
SIMPLE
|
|
TRENDY
|
|
}
|
|
|
|
enum Platform {
|
|
INSTAGRAM
|
|
FACEBOOK
|
|
KAKAO
|
|
BLOG
|
|
}
|
|
}
|
|
|
|
package "Application Layer" <<Rectangle>> {
|
|
package "Use Cases (Input Port)" {
|
|
interface GenerateImagesUseCase {
|
|
+ execute(command: ContentCommand.GenerateImages): JobInfo
|
|
}
|
|
|
|
interface GetJobStatusUseCase {
|
|
+ execute(jobId: String): JobInfo
|
|
}
|
|
|
|
interface GetEventContentUseCase {
|
|
+ execute(eventId: String): ContentInfo
|
|
}
|
|
|
|
interface GetImageListUseCase {
|
|
+ execute(eventId: String, style: ImageStyle, platform: Platform): List<ImageInfo>
|
|
}
|
|
|
|
interface DeleteImageUseCase {
|
|
+ execute(imageId: Long): void
|
|
}
|
|
|
|
interface RegenerateImageUseCase {
|
|
+ execute(command: ContentCommand.RegenerateImage): JobInfo
|
|
}
|
|
}
|
|
|
|
package "Ports (Output Port)" {
|
|
interface ContentReader {
|
|
+ findByEventDraftIdWithImages(eventId: String): Optional<Content>
|
|
}
|
|
|
|
interface ContentWriter {
|
|
+ save(content: Content): Content
|
|
+ saveImage(image: GeneratedImage): GeneratedImage
|
|
}
|
|
|
|
interface JobReader {
|
|
+ getJob(jobId: String): Optional<RedisJobData>
|
|
}
|
|
|
|
interface JobWriter {
|
|
+ saveJob(jobData: RedisJobData, ttlSeconds: long): void
|
|
+ updateJobStatus(jobId: String, status: String, progress: Integer): void
|
|
}
|
|
|
|
interface CDNUploader {
|
|
+ upload(imageData: byte[], fileName: String): String
|
|
}
|
|
}
|
|
|
|
package "Service Implementation" {
|
|
class StableDiffusionImageGenerator implements GenerateImagesUseCase {
|
|
- replicateClient: ReplicateApiClient
|
|
- cdnUploader: CDNUploader
|
|
- jobWriter: JobWriter
|
|
- contentWriter: ContentWriter
|
|
+ execute(command: ContentCommand.GenerateImages): JobInfo
|
|
}
|
|
|
|
class JobManagementService implements GetJobStatusUseCase {
|
|
- jobReader: JobReader
|
|
+ execute(jobId: String): JobInfo
|
|
}
|
|
|
|
class GetEventContentService implements GetEventContentUseCase {
|
|
- contentReader: ContentReader
|
|
+ execute(eventId: String): ContentInfo
|
|
}
|
|
|
|
class GetImageListService implements GetImageListUseCase {
|
|
- imageReader: ImageReader
|
|
+ execute(eventId: String, style: ImageStyle, platform: Platform): List<ImageInfo>
|
|
}
|
|
|
|
class DeleteImageService implements DeleteImageUseCase {
|
|
- imageWriter: ImageWriter
|
|
+ execute(imageId: Long): void
|
|
}
|
|
|
|
class RegenerateImageService implements RegenerateImageUseCase {
|
|
- imageReader: ImageReader
|
|
- imageWriter: ImageWriter
|
|
- jobWriter: JobWriter
|
|
+ execute(command: ContentCommand.RegenerateImage): JobInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
package "Infrastructure Layer" <<Rectangle>> {
|
|
class RedisGateway implements ContentReader, ContentWriter, JobReader, JobWriter {
|
|
- redisTemplate: RedisTemplate<String, Object>
|
|
- objectMapper: ObjectMapper
|
|
+ getAIRecommendation(eventId: String): Optional<Map<String, Object>>
|
|
+ saveJob(jobData: RedisJobData, ttlSeconds: long): void
|
|
+ getJob(jobId: String): Optional<RedisJobData>
|
|
+ save(content: Content): Content
|
|
+ saveImage(image: GeneratedImage): GeneratedImage
|
|
}
|
|
|
|
class ReplicateApiClient {
|
|
- apiToken: String
|
|
- baseUrl: String
|
|
+ createPrediction(request: ReplicateRequest): ReplicateResponse
|
|
+ getPrediction(predictionId: String): ReplicateResponse
|
|
}
|
|
|
|
class AzureBlobStorageUploader implements CDNUploader {
|
|
- connectionString: String
|
|
- containerName: String
|
|
- circuitBreaker: CircuitBreaker
|
|
+ upload(imageData: byte[], fileName: String): String
|
|
}
|
|
}
|
|
|
|
package "Presentation Layer" <<Rectangle>> {
|
|
class ContentController {
|
|
- generateImagesUseCase: GenerateImagesUseCase
|
|
- getJobStatusUseCase: GetJobStatusUseCase
|
|
- getEventContentUseCase: GetEventContentUseCase
|
|
- getImageListUseCase: GetImageListUseCase
|
|
- deleteImageUseCase: DeleteImageUseCase
|
|
- regenerateImageUseCase: RegenerateImageUseCase
|
|
+ generateImages(command: ContentCommand.GenerateImages): ResponseEntity<JobInfo>
|
|
+ getJobStatus(jobId: String): ResponseEntity<JobInfo>
|
|
+ getContentByEventId(eventId: String): ResponseEntity<ContentInfo>
|
|
+ deleteImage(imageId: Long): ResponseEntity<Void>
|
|
}
|
|
}
|
|
|
|
' ============================================
|
|
' 관계 정의
|
|
' ============================================
|
|
Content "1" o-- "0..*" GeneratedImage : contains
|
|
GeneratedImage --> ImageStyle
|
|
GeneratedImage --> Platform
|
|
|
|
ContentController ..> GenerateImagesUseCase
|
|
ContentController ..> GetJobStatusUseCase
|
|
ContentController ..> GetEventContentUseCase
|
|
ContentController ..> GetImageListUseCase
|
|
ContentController ..> DeleteImageUseCase
|
|
ContentController ..> RegenerateImageUseCase
|
|
|
|
StableDiffusionImageGenerator ..> CDNUploader
|
|
StableDiffusionImageGenerator ..> JobWriter
|
|
StableDiffusionImageGenerator ..> ContentWriter
|
|
StableDiffusionImageGenerator --> ReplicateApiClient
|
|
|
|
JobManagementService ..> JobReader
|
|
GetEventContentService ..> ContentReader
|
|
GetImageListService ..> "ImageReader\n(extends ContentReader)"
|
|
DeleteImageService ..> "ImageWriter\n(extends ContentWriter)"
|
|
RegenerateImageService ..> "ImageReader\n(extends ContentReader)"
|
|
RegenerateImageService ..> "ImageWriter\n(extends ContentWriter)"
|
|
RegenerateImageService ..> JobWriter
|
|
|
|
RedisGateway ..|> ContentReader
|
|
RedisGateway ..|> ContentWriter
|
|
RedisGateway ..|> JobReader
|
|
RedisGateway ..|> JobWriter
|
|
AzureBlobStorageUploader ..|> CDNUploader
|
|
|
|
' ============================================
|
|
' 레이어 의존성 방향
|
|
' ============================================
|
|
note top of Content : **Domain Layer**\n순수 비즈니스 로직\n외부 의존성 없음
|
|
note top of "Use Cases (Input Port)" : **Application Layer**\nUse Case 인터페이스 (Input Port)\n비즈니스 흐름 정의
|
|
note top of "Ports (Output Port)" : **Application Layer**\n외부 시스템 추상화 (Output Port)\n의존성 역전 원칙
|
|
note top of RedisGateway : **Infrastructure Layer**\nOutput Port 구현체\nRedis 저장소 연동
|
|
note top of ContentController : **Presentation Layer**\nREST API 컨트롤러\nHTTP 요청 처리
|
|
|
|
note bottom of ContentController : **의존성 방향**\nPresentation → Application → Domain\nInfrastructure → Application\n\n**핵심 원칙**\n• Domain은 다른 레이어에 의존하지 않음\n• Application은 Domain에만 의존\n• Infrastructure는 Application Port 구현\n• Presentation은 Application Use Case 호출
|
|
|
|
@enduml
|