# Distribution Service 데이터베이스 설계서

## 📋 데이터설계 요약

### 설계 개요
- **서비스명**: Distribution Service
- **아키텍처 패턴**: Layered Architecture
- **데이터베이스**: PostgreSQL (배포 상태 영구 저장), Redis (실시간 모니터링)
- **설계 일시**: 2025-10-29
- **설계자**: Backend Developer (최수연 "아키텍처")

### 데이터 특성
- **배포 상태 관리**: 이벤트별 다중 채널 배포 상태 추적
- **채널 독립성**: 6개 채널(TV, CALL, SNS)별 독립적 상태 관리
- **실시간 모니터링**: Redis 캐시를 통한 배포 진행 상태 실시간 조회
- **성과 추적**: 채널별 도달률(estimatedViews), 완료 시간, 재시도 횟수 추적
- **에러 관리**: 채널별 에러 메시지, 재시도 정보 저장

### 주요 테이블
1. **distribution_status**: 배포 전체 상태 관리 (이벤트 ID, 전체 상태, 시작/완료 시간)
2. **channel_status**: 채널별 세부 배포 상태 (채널 타입, 진행률, 배포 ID, 도달률, 에러 정보)

### 캐시 설계
- **event:{eventId}:distribution**: 배포 상태 실시간 조회 (TTL: 1시간)
- **distribution:channel:{eventId}:{channel}**: 채널별 상태 캐시 (TTL: 30분)

---

## 1. 데이터베이스 스키마 설계

### 1.1 PostgreSQL 테이블 설계

#### 1.1.1 distribution_status (배포 상태 테이블)

**테이블 목적**: 이벤트별 배포 전체 상태 관리

| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | BIGSERIAL | NO | - | 배포 상태 ID (PK) |
| event_id | VARCHAR(36) | NO | - | 이벤트 ID (UUID) |
| overall_status | VARCHAR(20) | NO | - | 전체 배포 상태 (IN_PROGRESS, COMPLETED, FAILED, PARTIAL_SUCCESS) |
| started_at | TIMESTAMP | NO | - | 배포 시작 시간 |
| completed_at | TIMESTAMP | YES | NULL | 배포 완료 시간 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 생성 시간 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 수정 시간 |

**제약 조건**:
- PRIMARY KEY: id
- UNIQUE KEY: event_id (이벤트당 하나의 배포 상태만 존재)
- INDEX: event_id (조회 성능 최적화)
- CHECK: overall_status IN ('IN_PROGRESS', 'COMPLETED', 'FAILED', 'PARTIAL_SUCCESS')

#### 1.1.2 channel_status (채널 배포 상태 테이블)

**테이블 목적**: 채널별 세부 배포 상태 및 성과 추적

| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | BIGSERIAL | NO | - | 채널 상태 ID (PK) |
| distribution_status_id | BIGINT | NO | - | 배포 상태 ID (FK) |
| channel | VARCHAR(20) | NO | - | 채널 타입 (URIDONGNETV, RINGOBIZ, GINITV, INSTAGRAM, NAVER, KAKAO) |
| status | VARCHAR(20) | NO | - | 채널 배포 상태 (PENDING, IN_PROGRESS, SUCCESS, FAILED) |
| progress | INTEGER | YES | 0 | 진행률 (0-100) |
| distribution_id | VARCHAR(100) | YES | NULL | 채널별 배포 ID (외부 시스템 ID) |
| estimated_views | INTEGER | YES | 0 | 예상 도달률 (조회수) |
| update_timestamp | TIMESTAMP | NO | CURRENT_TIMESTAMP | 상태 업데이트 시간 |
| event_id | VARCHAR(36) | NO | - | 이벤트 ID (조회 최적화용) |
| impression_schedule | TEXT | YES | NULL | 노출 일정 (JSON 배열) |
| post_url | VARCHAR(500) | YES | NULL | 게시물 URL |
| post_id | VARCHAR(100) | YES | NULL | 게시물 ID |
| message_id | VARCHAR(100) | YES | NULL | 메시지 ID (카카오톡) |
| completed_at | TIMESTAMP | YES | NULL | 채널 배포 완료 시간 |
| error_message | TEXT | YES | NULL | 에러 메시지 |
| retries | INTEGER | YES | 0 | 재시도 횟수 |
| last_retry_at | TIMESTAMP | YES | NULL | 마지막 재시도 시간 |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 생성 시간 |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 수정 시간 |

**제약 조건**:
- PRIMARY KEY: id
- FOREIGN KEY: distribution_status_id REFERENCES distribution_status(id) ON DELETE CASCADE
- UNIQUE KEY: (distribution_status_id, channel) - 배포당 채널별 하나의 상태만 존재
- INDEX: event_id (이벤트별 조회 최적화)
- INDEX: (event_id, channel) (채널별 조회 최적화)
- INDEX: status (상태별 조회 최적화)
- CHECK: channel IN ('URIDONGNETV', 'RINGOBIZ', 'GINITV', 'INSTAGRAM', 'NAVER', 'KAKAO')
- CHECK: status IN ('PENDING', 'IN_PROGRESS', 'SUCCESS', 'FAILED')
- CHECK: progress BETWEEN 0 AND 100

---

### 1.2 Redis 캐시 설계

#### 1.2.1 배포 상태 캐시

**키 패턴**: `event:{eventId}:distribution`

**데이터 구조**: Hash
```json
{
  "eventId": "uuid",
  "overallStatus": "IN_PROGRESS",
  "startedAt": "2025-10-29T10:00:00",
  "completedAt": null,
  "successCount": 3,
  "failureCount": 1,
  "totalChannels": 6
}
```

**TTL**: 1시간 (3600초)

**사용 목적**:
- 배포 상태 실시간 조회 성능 최적화
- DB 부하 감소 (조회 빈도가 높은 데이터)
- 배포 진행 중 빠른 상태 업데이트

#### 1.2.2 채널별 상태 캐시

**키 패턴**: `distribution:channel:{eventId}:{channel}`

**데이터 구조**: Hash
```json
{
  "channel": "INSTAGRAM",
  "status": "IN_PROGRESS",
  "progress": 75,
  "distributionId": "ig_post_12345",
  "estimatedViews": 5000,
  "updateTimestamp": "2025-10-29T10:30:00",
  "postUrl": "https://instagram.com/p/abc123",
  "errorMessage": null
}
```

**TTL**: 30분 (1800초)

**사용 목적**:
- 채널별 배포 상태 실시간 모니터링
- 진행률 추적 및 업데이트
- 외부 API 호출 결과 임시 저장

---

## 2. Entity-Table 매핑

### 2.1 DistributionStatus Entity → distribution_status Table

| Entity 필드 | 테이블 컬럼 | 매핑 |
|-------------|-------------|------|
| id | id | 1:1 |
| eventId | event_id | 1:1 |
| overallStatus | overall_status | 1:1 |
| startedAt | started_at | 1:1 |
| completedAt | completed_at | 1:1 |
| channels | (관계) | 1:N → channel_status |
| createdAt | created_at | 1:1 (BaseTimeEntity) |
| updatedAt | updated_at | 1:1 (BaseTimeEntity) |

### 2.2 ChannelStatusEntity Entity → channel_status Table

| Entity 필드 | 테이블 컬럼 | 매핑 |
|-------------|-------------|------|
| id | id | 1:1 |
| distributionStatus | distribution_status_id | N:1 (FK) |
| channel | channel | 1:1 (Enum) |
| status | status | 1:1 |
| progress | progress | 1:1 |
| distributionId | distribution_id | 1:1 |
| estimatedViews | estimated_views | 1:1 |
| updateTimestamp | update_timestamp | 1:1 |
| eventId | event_id | 1:1 |
| impressionSchedule | impression_schedule | 1:1 (JSON String) |
| postUrl | post_url | 1:1 |
| postId | post_id | 1:1 |
| messageId | message_id | 1:1 |
| completedAt | completed_at | 1:1 |
| errorMessage | error_message | 1:1 |
| retries | retries | 1:1 |
| lastRetryAt | last_retry_at | 1:1 |
| createdAt | created_at | 1:1 (BaseTimeEntity) |
| updatedAt | updated_at | 1:1 (BaseTimeEntity) |

---

## 3. 데이터 관계

### 3.1 테이블 간 관계

```
distribution_status (1) ----< (N) channel_status
  - 하나의 배포 상태는 여러 채널 상태를 가짐
  - CASCADE DELETE: 배포 상태 삭제 시 채널 상태도 함께 삭제
```

### 3.2 인덱스 전략

**distribution_status 테이블**:
- PRIMARY KEY: id (클러스터 인덱스)
- UNIQUE INDEX: event_id (이벤트별 배포 상태 유일성 보장)

**channel_status 테이블**:
- PRIMARY KEY: id (클러스터 인덱스)
- UNIQUE INDEX: (distribution_status_id, channel) (배포당 채널별 유일성)
- INDEX: event_id (이벤트별 채널 상태 조회 최적화)
- INDEX: (event_id, channel) (복합 조회 최적화)
- INDEX: status (상태별 필터링 최적화)

---

## 4. 데이터 독립성 설계

### 4.1 서비스 간 데이터 분리

**Distribution Service 데이터 소유권**:
- 배포 상태 및 채널별 성과 데이터 완전 소유
- 타 서비스의 데이터를 직접 조회하지 않음

**타 서비스 데이터 참조**:
- **Event ID**: Event Service에서 생성한 ID를 참조 (FK 없음)
- **User ID**: 직접 저장하지 않음 (인증 정보로만 사용)
- **참조 방식**: Redis 캐시 또는 Kafka 이벤트로만 참조

### 4.2 데이터 동기화 전략

**Kafka 이벤트 발행**:
```java
// 배포 완료 시 이벤트 발행
Topic: distribution-completed
Event: {
  "eventId": "uuid",
  "distributedChannels": [
    {
      "channel": "INSTAGRAM",
      "status": "SUCCESS",
      "expectedViews": 5000
    }
  ],
  "completedAt": "2025-10-29T11:00:00"
}
```

**Analytics Service 연동**:
- Distribution Service → Kafka → Analytics Service
- 채널별 성과 데이터 비동기 전달
- 장애 격리 보장 (Circuit Breaker)

---

## 5. 쿼리 성능 최적화

### 5.1 조회 쿼리 최적화

**이벤트별 배포 상태 조회**:
```sql
-- 인덱스 활용: event_id
SELECT ds.*, cs.*
FROM distribution_status ds
LEFT JOIN channel_status cs ON ds.id = cs.distribution_status_id
WHERE ds.event_id = ?;
```

**채널별 배포 현황 조회**:
```sql
-- 인덱스 활용: (event_id, channel)
SELECT *
FROM channel_status
WHERE event_id = ? AND channel = ?;
```

**진행 중인 배포 목록 조회**:
```sql
-- 인덱스 활용: overall_status
SELECT *
FROM distribution_status
WHERE overall_status = 'IN_PROGRESS'
ORDER BY started_at DESC;
```

### 5.2 캐시 전략

**조회 우선순위**:
1. Redis 캐시 조회 시도
2. 캐시 미스 시 PostgreSQL 조회
3. 조회 결과를 Redis에 캐싱

**캐시 무효화**:
- 배포 상태 업데이트 시 캐시 갱신
- 배포 완료 시 캐시 TTL 연장 (1시간 → 24시간)
- 채널 상태 변경 시 해당 채널 캐시 갱신

---

## 6. 데이터 보안 및 제약

### 6.1 데이터 무결성

**NOT NULL 제약**:
- 필수 정보: event_id, channel, status, overall_status
- 시간 정보: started_at, update_timestamp

**CHECK 제약**:
- overall_status: 4가지 상태만 허용
- channel: 6개 채널 타입만 허용
- status: 4가지 배포 상태만 허용
- progress: 0-100 범위 제한

**UNIQUE 제약**:
- event_id: 이벤트당 하나의 배포 상태
- (distribution_status_id, channel): 배포당 채널별 하나의 상태

### 6.2 CASCADE 정책

**ON DELETE CASCADE**:
- distribution_status 삭제 시 channel_status 자동 삭제
- 데이터 일관성 보장

---

## 7. 마이그레이션 전략

### 7.1 초기 데이터 마이그레이션
- 초기 배포 시 기본 데이터 없음 (운영 데이터만 존재)
- 채널 타입 Enum 검증 데이터 확인

### 7.2 스키마 변경 전략
- Flyway 또는 Liquibase를 통한 버전 관리
- 무중단 배포를 위한 Blue-Green 전략
- 인덱스 추가 시 CONCURRENTLY 옵션 사용

---

## 8. 모니터링 및 유지보수

### 8.1 성능 모니터링 지표
- 배포 상태 조회 응답 시간 (<100ms)
- 채널별 배포 성공률 (>95%)
- 재시도 횟수 평균 (<2회)
- 캐시 히트율 (>80%)

### 8.2 데이터 정리 정책
- 완료된 배포 상태: 30일 후 아카이빙
- 실패한 배포 로그: 90일 보관
- Redis 캐시: TTL 자동 만료

---

## 9. 참고 자료

### 9.1 관련 문서
- 클래스 설계서: `design/backend/class/distribution-service.puml`
- API 설계서: `design/backend/api/distribution-service-api.md`
- 시퀀스 다이어그램: `design/backend/sequence/inner/distribution-service-*.puml`

### 9.2 외부 참조
- PostgreSQL 공식 문서: https://www.postgresql.org/docs/
- Redis 캐시 설계 가이드: https://redis.io/docs/manual/patterns/

---

**문서 버전**: v1.0
**작성일**: 2025-10-29
**작성자**: Backend Developer (최수연 "아키텍처")
