ADR-003 goods·ticketing 제공 인터페이스 = UseCase 경계
- 상태: 채택
- 날짜: 2026-07-03
- 근거: PRD FR-4 / TDD “FR-4 결정” / B2B 파트너 연동 PRD FR-4·FR-5
맥락
B2B 파트너 연동(② 과제)이 상품·티켓 등록을 기존 도메인 로직을 경유해 수행한다. 노출 경계를 UseCase로 할지 DomainService로 할지 확정이 필요하다. 두 진입점의 소유권 해석 방식이 비대칭이다:
CreateMyProductUseCase.execute(command)→OwnershipGuard.authUserId()로 SecurityContext에서 소유자 해석.CreateMyEventUseCase.execute(command)→command.ownerUserId(Command 인자)로 소유자 전달.
결정
제공 경계는 UseCase 레이어로 확정한다.
| 도메인 | 제공 UseCase |
|---|---|
| goods | CreateMyProductUseCase |
| ticketing | CreateMyEventUseCase |
두 UseCase의 시그니처·반환 타입을 계약으로 동결하고, ArchUnit/계약 테스트(BE-06)가 회귀를 감시한다.
근거
- 형제 PRD 정합: ② PRD FR-4/FR-5가 두 UseCase를 “코드 변경 없이 그대로 호출”, 경유 접점을 인증 계층으로 명시. 실제 소비 지점이 UseCase.
- 트랜잭션·소유권 경계 일치: UseCase가
@Transactional과OwnershipGuard소유권 해석을 소유. DomainService 노출 시 파트너 컨텍스트가 이 오케스트레이션을 중복 구현. - 캡슐화: DomainService는 도메인 내부 협력자. 컨텍스트 밖 노출은 경계 붕괴.
- 비대칭 흡수: 소유권 해석 비대칭은 파트너 인증 필터가 SecurityContext를 연동 전용 User로 채우는 것으로 통일 해결. 두 UseCase 코드 무변경.
대안 (미채택)
- DomainService 노출: 트랜잭션·소유권 오케스트레이션 우회·중복. 도메인 내부 유출.
- 파트너 전용 신규 UseCase 신설: 등록 로직 중복(② PRD가 명시 금지 — “중복 구현하지 않는다”).
영향
- ② 과제는 신규 Partner 도메인(지원)에서 인증 계층만 구현하고 두 UseCase를 그대로 경유.
booking·payment는 소비처 없어 제공 인터페이스 미정의(오버엔지니어링 회피, 필요 시점 정의).- BE-06 계약 테스트가 두 UseCase 시그니처 변경을 조기 감지.
변경 이력 — v2 (BE-03, sellerType 계약 진화)
CreateMyProductCommand에 sellerType: SellerType? 필드를 추가했다. ProvidedInterfaceContractTest가
필드 존재를 리플렉션으로 동결 검증한다(execute 시그니처의 파라미터 타입은 CreateMyProductCommand
클래스 동일성만 비교하므로 필드 추가로 깨지지 않는다 — GREEN 유지).
- 실제 판별 로직은 Command가 아니라 domain이 소유한다:
GoodsDomainService.createProduct가 주입받은AuthChannelResolver.isPartnerAuthenticated()를 직접 조회해SellerType을 결정하고Product.create에 전달한다(domain.common.security.AuthChannelResolverJavadoc “하위 레이어 (DomainService)가 이 인터페이스를 통해 조회한다”와 일치).CreateMyProductUseCase는 변경 없음. - 따라서
Command.sellerType은 현재 쓰기 경로에서 사용되지 않는다 — 계약 형태(필드 존재)만 예약해 두는 선택이다. 향후 운영 도구 등에서 명시적 override가 필요해지면 이 필드를 실제로 소비하도록 UseCase를 확장할 수 있다. - 이 결정은 TDD 시퀀스 다이어그램(“Ctl→R: isPartnerAuthenticated()“)과 다르다 — Controller가 아닌
DomainService가 Resolver를 소유하도록 조정했다. Controller 계층은 domain interface를 직접 의존하지
않는다(레이어 규칙
presentation → application만 허용).