[BE-44] 수집 cycle state-write 영속 계약 보완

작업 내용 (설계 의도)

변경 사항

병합된 BE-33에는 aggregate/JPA mapping만 있고 dispatcher가 필요한 prepare/save·active-source claim·terminal failure/budget·SLA cutoff gate 계약이 없다. TDD 20260730-확장-안전-공고수집-배치-tdd.mdJobPostingCollectionCycleRepository 확장과 CollectionCycleStateWriteDomainService를 추가한다. 이 티켓은 BE-34가 UseCase에서 domain service만 호출하며 active 재검증, lease fencing, 동일 KST 재시도 금지 terminalization을 영속할 수 있게 하는 최소 선행 산출물이다.

CollectionSlaCutoffGate는 posting domain interface로만 정의한다. BE-43이 MySQL SLA header lock 구현체를 제공하고, BE-34는 이 contract만 소비한다. 이 티켓은 dispatcher orchestration·snapshot capture·operation API·config/scheduler wiring을 구현하지 않는다.

의존

  • BE-33
  • DB-01 cycle/SLA header schema

다이어그램

처리 흐름

sequenceDiagram
    participant D as StateWriteDomainService
    participant G as SlaCutoffGate
    participant R as CycleRepository
    D->>G: withDailyWriteGate(runDate)
    G->>R: save/claim/fail/exhaust
    R-->>D: fenced result

클래스 의존

flowchart LR
    StateWriteDomainService --> CollectionSlaCutoffGate
    StateWriteDomainService --> CycleRepository
    CycleRepositoryImpl -.-> CycleRepository
    SlaGateImpl -.-> CollectionSlaCutoffGate

테스트 케이스

  • createIfAbsent와 save는 같은 source/KST 일자에 하나의 cycle만 유지한다.
  • claimReady는 active jobSourceId 집합 밖 cycle을 lease하지 않는다.
  • 다른 또는 만료 lease token의 apply/fail/exhaust write는 0건으로 거부된다.
  • HTTP/lease failure는 gate 안에서 terminal FAILED로 저장되고 당일 claim 대상에서 제외된다.
  • posting budget 초과는 gate 안에서 BUDGET_EXHAUSTED로 저장되고 source health failure를 만들지 않는다.
  • 모든 cycle state write는 CollectionSlaCutoffGate를 거치며 BE-43 구현이 없는 경우 domain test는 fake gate로 검증한다.