ADR-DS-015_무결성 및 불변 조건 검증 규칙

ADR-DS-015_무결성 및 불변 조건 검증 규칙

## 1. ?? ??

  • ADR ID: ADR-DS-015
  • ??: 무결성 및 불변 조건 검증 규칙
  • ??: Draft
  • ?? ??: 2026-03-02
  • ?? ??: Domain Architecture
  • ?? ADR: ?? ??
  • Supersedes: N/A
  • Superseded By: N/A

2. ?? (Context)

? ?? ?? ?? ??/?? ?? ADR ?? ?? v2 ?? ???? ?? ??/?? ?? ?? ??. ?? ??/?? ?? ?? ??/?? ?? ?? ??.


  • 상태: Draft

  • 작성일: 2026-03-02

  • 범위: 데이터 불변 조건, DB 제약, 트랜잭션 방어, 주기적 검증, 진단/복구 전략(운영용)


1. 목적

이 문서는 다음을 고정한다.

  • 시스템이 항상 만족해야 하는 불변 조건(invariants)

  • 불변 조건을 DB 레벨에서 어떻게 강제할지

  • 런타임(서버 로직)에서 어떻게 검증할지

  • 깨짐을 어떻게 탐지하고 진단할지

  • 복구/정정 절차를 어떤 원칙으로 수행할지


2. 핵심 원칙

  1. 가능한 불변 조건은 DB 제약으로 강제한다.

  2. 상태 변경은 항상 서버 트랜잭션으로 수행한다.

  3. “검증 없는 쓰기”를 허용하지 않는다.

  4. 무결성 검증은 배치로도 수행한다(조용한 깨짐 탐지).

  5. 복구는 임의 수정이 아니라 규칙 기반 정정이다.


3. 핵심 불변 조건 목록 (Invariants)

3.1 Slot 수량 불변 조건

I-S1

0 <= slot.remainingQuantity <= slot.totalQuantity

I-S2

slot.totalQuantity > 0

3.2 Reservation 상태/필드 불변 조건

I-R1 (상태 단일성)
reservation.status는 아래 중 하나

RESERVED | REDEEMED | EXPIRED | CANCELLED

I-R2 (REDEEMED 필드 조건)

status == REDEEMED -> redeemedAt != null
status == REDEEMED -> redeemedAt <= expiresAt

I-R3 (EXPIRED 필드 조건)

status == EXPIRED -> redeemedAt == null

I-R4 (RESERVED 필드 조건)

status == RESERVED -> redeemedAt == null

I-R5 (CANCELLED 필드 조건)

status == CANCELLED -> redeemedAt == null

3.3 Slot-Reservation 정합성 불변 조건

I-SR1 (차감 시점)
slot.remainingQuantity 감소는 RESERVED→REDEEMED 전이에서만 발생한다.

I-SR2 (총량 상한)

COUNT(reservation where status in {REDEEMED} and slotId = X) <= slot.totalQuantity

I-SR3 (수량 정합성, 선택)

slot.remainingQuantity == slot.totalQuantity - COUNT(redeemed for slot)

주의: I-SR3은 구현/운영 편의에 따라 “진단용”으로 둘 수 있다.
(부분 취소/관리자 조정 기능이 생기면 항상 참이 아닐 수 있음)


3.4 사용자 동시 확보 불변 조건

I-U1

userId 기준 status==RESERVED 인 reservation은 최대 1개

4. DB 레벨 강제 규칙 (Constraints)

가능한 범위에서 DB가 강제해야 한다.

4.1 CHECK 제약 (권장)

  • Slot

    • remainingQuantity >= 0

    • remainingQuantity <= totalQuantity

    • totalQuantity > 0

  • Reservation

    • status enum 제한

    • status별 필드 조건은 부분적으로 CHECK로 표현 가능

주의:

  • 상태별 필드 조건은 복잡해질 수 있으므로
    핵심만 DB로 강제하고, 나머지는 RPC에서 강제한다.

4.2 UNIQUE 제약 (권장)

  • 사용자 동시 확보 1개 강제(부분 인덱스 활용)

    • (userId) where status=‘RESERVED’ unique

이 제약은 동시 요청에서 매우 중요하다.


4.3 FK 제약 (필수)

  • reservation.slotId → slot.slotId

  • slot.merchantId → merchant.merchantId


5. 서버 로직 강제 규칙 (Runtime Guards)

DB에서 다 못 막는 규칙은 서버(RPC)에서 강제한다.

5.1 상태 전이 검증

  • ADR-DS-010 전이표 외 전이는 모두 거부

  • 전이 시 필요한 필드만 세팅, 나머지 필드는 불변

5.2 수량 차감 원자성

  • ADR-DS-011 준수

  • 차감과 REDEEMED 전이는 같은 트랜잭션

5.3 멱등성

  • reservationId 기반 중복 요청은 상태를 깨지 않음

  • slot 차감은 최대 1회


6. 무결성 진단(배치 검증) 규칙

MVP 이후 운영 단계에서 필요하다.

6.1 진단 주기

  • 하루 1회(권장)

  • 또는 1시간 1회(트래픽 증가 시)

6.2 진단 항목

  • D1: remainingQuantity 음수/상한 초과 탐지 (I-S1)

  • D2: 상태-필드 불일치 탐지 (I-R2~I-R5)

  • D3: REDEEMED 수가 totalQuantity 초과 탐지 (I-SR2)

  • D4: user RESERVED 중복 탐지 (I-U1)

6.3 진단 결과 저장

  • integrity_reports 테이블(또는 로그)에 기록

  • 항목별 count 및 샘플 reservationId/slotId 저장


7. 복구 원칙 (Repair Policy)

복구는 “임의 편집”이 아니라 규칙 기반이다.

7.1 자동 복구 허용 범위(초기)

다음은 자동 복구를 허용할 수 있다.

  • R1: RESERVED인데 expiresAt이 지났고 status가 RESERVED로 남아있는 경우

    • 서버 기준 EXPIRED로 전환(정상 만료 누락 보정)
  • R2: status==REDEEMED인데 redeemedAt이 null인 경우

    • 데이터가 깨진 상태이므로 자동 복구 금지

    • 운영자 검토 대상으로 플래그

  • R3: remainingQuantity < 0

    • 자동 복구 금지(원인 파악 필요)

7.2 운영자 검토 원칙

자동 복구 금지 항목은 다음으로 처리한다.

  • 원인 파악(중복 요청, 트랜잭션 실패, 수동 조작)

  • 정정은 “정책적으로 의미 있는 상태”로만 이동

  • 모든 정정은 audit log에 남김


8. 감사 로그(Audit) 규칙

상태/수량/플랜/제재 변경은 항상 로그로 남긴다.

  • entityType (slot/reservation/user/merchant)

  • entityId

  • action (transition, decrement, penalty_apply, plan_change)

  • before/after snapshot(요약)

  • actor (system/admin/user)

  • createdAt


9. 필수 테스트 케이스

T1: 동시에 사용 확정 2건에서 remaining 음수 방지
T2: user RESERVED 2개 생성 시 UNIQUE 제약으로 차단
T3: expired 판정 누락 시 배치가 RESERVED→EXPIRED 보정
T4: REDEEMED인데 redeemedAt null인 데이터 탐지 보고
T5: REDEEMED count가 totalQuantity 초과하는 이상치 탐지


10. 미결 항목

  • 배치 검증을 어디서 실행할지(Edge/Worker/외부 크론)

  • 운영자용 무결성 대시보드 필요 여부

  • 수동 조정 기능 도입 시 I-SR3 유지 여부



Template v2 Addendum

In Scope

  • Core domain rules (state/time/quantity/penalty/integrity/retention)

Out of Scope

  • Payment settlement, PG integration, UI behavior

Boundaries

  • Domain ADR defines policy. Implementation details belong to Tech ADR and code.

Source of Truth (SoT)

  • State model: ADR-DS-010; Time/expiry: ADR-DS-012; Quantity/concurrency: ADR-DS-064.

Validation

  • Detect state/constraint violations via tests and batch checks.
  • Recovery and reprocessing must be idempotent.

Revisit Conditions

  • KPI threshold breach (reservation failure, expiry ratio, payment confirmation failure)
  • External dependency change (PG, regulation, location API)