ADR-003 goods·ticketing 제공 인터페이스 = UseCase 경계

맥락

B2B 파트너 연동(② 과제)이 상품·티켓 등록을 기존 도메인 로직을 경유해 수행한다. 노출 경계를 UseCase로 할지 DomainService로 할지 확정이 필요하다. 두 진입점의 소유권 해석 방식이 비대칭이다:

  • CreateMyProductUseCase.execute(command)OwnershipGuard.authUserId()로 SecurityContext에서 소유자 해석.
  • CreateMyEventUseCase.execute(command)command.ownerUserId(Command 인자)로 소유자 전달.

결정

제공 경계는 UseCase 레이어로 확정한다.

도메인제공 UseCase
goodsCreateMyProductUseCase
ticketingCreateMyEventUseCase

두 UseCase의 시그니처·반환 타입을 계약으로 동결하고, ArchUnit/계약 테스트(BE-06)가 회귀를 감시한다.

근거

  1. 형제 PRD 정합: ② PRD FR-4/FR-5가 두 UseCase를 “코드 변경 없이 그대로 호출”, 경유 접점을 인증 계층으로 명시. 실제 소비 지점이 UseCase.
  2. 트랜잭션·소유권 경계 일치: UseCase가 @TransactionalOwnershipGuard 소유권 해석을 소유. DomainService 노출 시 파트너 컨텍스트가 이 오케스트레이션을 중복 구현.
  3. 캡슐화: DomainService는 도메인 내부 협력자. 컨텍스트 밖 노출은 경계 붕괴.
  4. 비대칭 흡수: 소유권 해석 비대칭은 파트너 인증 필터가 SecurityContext를 연동 전용 User로 채우는 것으로 통일 해결. 두 UseCase 코드 무변경.

대안 (미채택)

  • DomainService 노출: 트랜잭션·소유권 오케스트레이션 우회·중복. 도메인 내부 유출.
  • 파트너 전용 신규 UseCase 신설: 등록 로직 중복(② PRD가 명시 금지 — “중복 구현하지 않는다”).

영향

  • ② 과제는 신규 Partner 도메인(지원)에서 인증 계층만 구현하고 두 UseCase를 그대로 경유.
  • booking·payment는 소비처 없어 제공 인터페이스 미정의(오버엔지니어링 회피, 필요 시점 정의).
  • BE-06 계약 테스트가 두 UseCase 시그니처 변경을 조기 감지.

변경 이력 — v2 (BE-03, sellerType 계약 진화)

CreateMyProductCommandsellerType: SellerType? 필드를 추가했다. ProvidedInterfaceContractTest가 필드 존재를 리플렉션으로 동결 검증한다(execute 시그니처의 파라미터 타입은 CreateMyProductCommand 클래스 동일성만 비교하므로 필드 추가로 깨지지 않는다 — GREEN 유지).

  • 실제 판별 로직은 Command가 아니라 domain이 소유한다: GoodsDomainService.createProduct가 주입받은 AuthChannelResolver.isPartnerAuthenticated()를 직접 조회해 SellerType을 결정하고 Product.create에 전달한다(domain.common.security.AuthChannelResolver Javadoc “하위 레이어 (DomainService)가 이 인터페이스를 통해 조회한다”와 일치). CreateMyProductUseCase는 변경 없음.
  • 따라서 Command.sellerType은 현재 쓰기 경로에서 사용되지 않는다 — 계약 형태(필드 존재)만 예약해 두는 선택이다. 향후 운영 도구 등에서 명시적 override가 필요해지면 이 필드를 실제로 소비하도록 UseCase를 확장할 수 있다.
  • 이 결정은 TDD 시퀀스 다이어그램(“Ctl→R: isPartnerAuthenticated()“)과 다르다 — Controller가 아닌 DomainService가 Resolver를 소유하도록 조정했다. Controller 계층은 domain interface를 직접 의존하지 않는다(레이어 규칙 presentation → application만 허용).