[BE-01] partner 도메인 모델 (Partner·PartnerApiKey·Repository·Gateway interface)

작업 내용 (설계 의도)

변경 사항

근거 TDD: ../TDD.md “시스템 역할 경계”·“인터페이스 시그니처”·“상태 전이 표”. 후행 티켓 전부가 import하는 partner 도메인 공통 계약을 한 티켓으로 확립한다(연관 병목 묶음).

  • domain/partner/entity/Partner.kt — Rich Domain. 필드 private var, private constructor + create(name, linkedUserId)·reconstitute(...). status: PartnerStatus, linkedUserId: Long. 메서드 suspend()·activate()·validateActive()(SUSPENDED면 예외). 시간은 캡슐화 메서드 내부 ZonedDateTime.now().
  • domain/partner/entity/PartnerApiKey.kt — Rich Domain. keyHash·status: ApiKeyStatus·revokedAt·lastUsedAt. 메서드 revoke()·recordUsage()·isActive(). create(partnerId, keyHash)·reconstitute(...).
  • domain/partner/entity/PartnerStatus.kt·ApiKeyStatus.kt — enum, 상태 전이는 enum 내부 canTransitTo() 캡슐화.
  • domain/partner/exception/*PartnerSuspendedException(403)·PartnerApiKeyInactiveException(401)·PartnerNotFoundException(404) 등.
  • domain/partner/repository/PartnerRepository.kt·PartnerApiKeyRepository.kt — interface(시그니처는 TDD 확정).
  • domain/partner/gateway/ApiKeyGenerator.kt — interface generateRandomPart()·hash()·matches().

의존

  • 없음 (선행 wave)

다이어그램

클래스 의존

flowchart LR
    Partner["Partner (Entity)"]
    Key["PartnerApiKey (Entity)"]
    Repo["PartnerRepository (interface)"]
    KeyRepo["PartnerApiKeyRepository (interface)"]
    Gen["ApiKeyGenerator (interface)"]
    Partner --> Repo
    Key --> KeyRepo
    Key --> Gen

테스트 케이스

  • ACTIVE Partner에 suspend()를 호출하면 상태가 SUSPENDED로 전이된다.
  • SUSPENDED Partner에 activate()를 호출하면 ACTIVE로 전이된다.
  • SUSPENDED Partner에 validateActive()를 호출하면 PartnerSuspendedException을 던진다.
  • ACTIVE PartnerApiKey에 revoke()를 호출하면 status=REVOKED·revokedAt이 채워진다.
  • 이미 REVOKED된 키에 revoke()를 재호출해도 상태가 변하지 않는다(멱등).
  • recordUsage() 호출 시 lastUsedAt이 이전 값보다 뒤 시각으로 갱신된다.
  • Partner.create는 status=ACTIVE로 생성한다.