@startuml !theme mono title 알림 발송 내부 시퀀스 (Event-Driven) queue "Event Hub<>" as EventHub participant "NotificationListener" as Listener participant "NotificationService" as Service participant "NotificationRouter" as Router participant "EmailNotifier" as EmailNotifier participant "Email Service<>" as Email participant "NotificationRepository" as Repository database "PostgreSQL<>" as DB EventHub -> Listener: consume(NotificationEvent) activate Listener note right Event 종류: - MeetingCreatedEvent - TodoAssignedEvent - MeetingReminderEvent - SummaryCompletedEvent end note Listener -> Service: processNotification(event) activate Service Service -> Repository: checkDuplicateNotification(eventId) activate Repository Repository -> DB: SELECT * FROM notifications\nWHERE event_id = ? note right 중복 발송 방지: - Event ID 기반 - Idempotency 보장 end note alt 중복 이벤트 DB --> Repository: notification exists Repository --> Service: duplicate Service --> Listener: skip (already processed) deactivate Service Listener --> EventHub: ACK deactivate Listener else 신규 이벤트 DB --> Repository: not found Repository --> Service: proceed deactivate Repository Service -> Service: parseEventPayload(event) note right 이벤트 파싱: - userId - notificationType - templateId - templateData end note Service -> Repository: getUserPreferences(userId) activate Repository Repository -> DB: SELECT * FROM user_notification_prefs\nWHERE user_id = ? note right 사용자 설정 확인: - 알림 채널 (email/sms) - 알림 활성화 여부 - 수신 시간대 end note DB --> Repository: preferences Repository --> Service: NotificationPreference deactivate Repository alt 알림 비활성화 Service --> Listener: skip (user preference) deactivate Service Listener --> EventHub: ACK deactivate Listener else 알림 활성화 Service -> Router: routeNotification(event, preferences) activate Router Router -> Router: determineChannel(preferences) note right 채널 선택: - 이메일 우선 - SMS 백업 end note alt 이메일 알림 Router -> EmailNotifier: sendEmail(notification) activate EmailNotifier EmailNotifier -> EmailNotifier: loadTemplate(templateId) note right 템플릿 로드: - 회의 초대 - 할일 배정 - 회의 알림 - 요약 완료 end note EmailNotifier -> EmailNotifier: renderTemplate(templateData) note right 템플릿 렌더링: - 제목 생성 - 본문 생성 - 다이나믹 데이터 삽입 end note EmailNotifier -> Email: send(to, subject, body) note right 이메일 발송: - SMTP 프로토콜 - 재시도 로직 - Timeout: 10s end note alt 발송 성공 Email --> EmailNotifier: success EmailNotifier --> Router: sent deactivate EmailNotifier else 발송 실패 Email --> EmailNotifier: failure EmailNotifier -> EmailNotifier: scheduleRetry() note right 재시도 전략: - Max retry: 3 - Backoff: exponential - 1m, 5m, 15m end note EmailNotifier --> Router: retry scheduled deactivate EmailNotifier end end Router --> Service: notification result deactivate Router Service -> Repository: saveNotificationLog(notification) activate Repository Repository -> DB: INSERT INTO notifications\n(event_id, user_id, type, channel, status, sent_at) note right 알림 로그 저장: - 발송 이력 - 채널 정보 - 성공/실패 상태 end note DB --> Repository: saved Repository --> Service: saved deactivate Repository Service -> Repository: updateUserActivity(userId, "NOTIFICATION_SENT") activate Repository Repository -> DB: INSERT INTO user_activities\n(user_id, activity_type, details) DB --> Repository: saved Repository --> Service: updated deactivate Repository Service --> Listener: processed deactivate Service Listener --> EventHub: ACK deactivate Listener end end @enduml