콘텐츠로 이동

Notification Schema

notification schema 는 알림 발송 요청과 사용자 선호도를 저장한다.

Schema purpose

  • PostgreSQL schemanotification
  • Corresponding domainengine/notification/
  • sqlc packagedb/queries/notification/

Event consumer 가 이벤트 수신 시 NotificationRequest 를 INSERT 하고, asynq 워커가 pickup 하여 발송.

Tables

notification.requests

발송 요청/이력.

CREATE TABLE notification.requests (
    id               UUID PRIMARY KEY,
    recipient_type   TEXT NOT NULL CHECK (recipient_type IN ('user_dm', 'guild_audit_channel', 'dashboard_toast')),
    recipient_id     TEXT NOT NULL,
    guild_id         UUID REFERENCES guild.guilds(id),
    template_key     TEXT NOT NULL,
    payload          JSONB NOT NULL,
    state            TEXT NOT NULL CHECK (state IN ('pending', 'sent', 'failed', 'suppressed')),
    attempt_count    INTEGER NOT NULL DEFAULT 0,
    last_error       TEXT,
    scheduled_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    sent_at          TIMESTAMPTZ,
    created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Columns

Column Type Constraints Description
id UUID v7 PK 요청 ID
recipient_type TEXT CHECK 발송 채널
recipient_id TEXT NOT NULL Discord user ID 또는 channel ID
guild_id UUID FK nullable 관련 길드
template_key TEXT NOT NULL 템플릿 식별자
payload JSONB NOT NULL 템플릿 변수
state TEXT CHECK 상태
attempt_count INTEGER NOT NULL 시도 횟수
last_error TEXT 마지막 실패 사유
scheduled_at TIMESTAMPTZ NOT NULL 예약 발송 시각
sent_at TIMESTAMPTZ 실제 발송 시각
created_at TIMESTAMPTZ NOT NULL

Indexes

Index Columns Purpose
requests_pkey (id) PK
requests_state_scheduled_idx (state, scheduled_at) WHERE state = 'pending' asynq 워커 pickup
requests_guild_created_idx (guild_id, created_at DESC) 길드별 최근 알림
requests_recipient_idx (recipient_type, recipient_id, created_at DESC) User 별 알림 이력

Foreign keys

Column References On delete
guild_id guild.guilds(id) SET NULL

Invariants

  • attempt_count <= 3
  • state = 'sent' 이면 sent_at IS NOT NULL
  • template_key 는 코드베이스 정의와 일치 (CI 검증)

notification.preferences

사용자별 알림 선호도.

CREATE TABLE notification.preferences (
    user_id                   UUID PRIMARY KEY REFERENCES identity.users(id),
    dm_enabled                BOOLEAN NOT NULL DEFAULT TRUE,
    payment_notifications     BOOLEAN NOT NULL DEFAULT TRUE,
    recovery_notifications    BOOLEAN NOT NULL DEFAULT TRUE,
    antinuke_notifications    BOOLEAN NOT NULL DEFAULT TRUE,
    marketing_notifications   BOOLEAN NOT NULL DEFAULT TRUE,
    updated_at                TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Columns

Column Type Constraints Description
user_id UUID PK, FK identity.users.id
dm_enabled BOOLEAN NOT NULL DM 전체 on/off
payment_notifications BOOLEAN NOT NULL 결제 알림
recovery_notifications BOOLEAN NOT NULL 복구 알림
antinuke_notifications BOOLEAN NOT NULL AntiNuke 일반 알림 (긴급은 override)
marketing_notifications BOOLEAN NOT NULL 마케팅성 알림
updated_at TIMESTAMPTZ NOT NULL

Foreign keys

Column References On delete
user_id identity.users(id) CASCADE

Invariants

  • 신규 User 등록 시 기본값으로 자동 생성 (또는 조회 시 lazy create)
  • AntiNuke 긴급 알림(antinuke.triggered) 은 이 선호도를 무시할 수 있음 (애플리케이션 레이어)

Relationships

erDiagram
    USERS ||--|| PREFERENCES : has
    USERS ||--o{ REQUESTS : "recipient of"
    GUILDS ||--o{ REQUESTS : "related to"

Cross-schema references

From To Semantics
notification.requests.guild_id guild.guilds.id 관련 길드
notification.preferences.user_id identity.users.id 사용자

Query patterns

  • InsertRequest — consumer 가 이벤트 수신 시
  • FetchPendingRequests — asynq 워커 pickup
  • MarkSent — 발송 성공
  • IncrementAttempt — 재시도 카운트
  • MarkFailed — 최대 시도 초과
  • GetPreference — 발송 전 확인
  • UpsertPreference — 사용자 대시보드에서 수정
  • DeleteOldRequests — 90일 경과 정리 cron

Data retention

  • Requests — 90일 후 삭제 (cron)
  • Preferences — User CASCADE

Migration history

Date Change Rationale
2026-04-xx 초기 스키마 MVP

See also

  • domain/notification.md — Notification 도메인
  • architecture/event-flow.md — Subscribers 컬럼
  • data/identity-schema.md — 참조하는 User
  • data/guild-schema.md — 참조하는 Guild