[BE-60] 소모임 예약 연동 — CommunityBooking + SlotInfoGateway

작업 내용 (설계 의도)

변경 사항

근거 TDD: “B3 community↔booking·SlotInfoGateway·실패 경로 비멤버 열람”. community가 기존 booking Slot을 ID로 연결해 모임 활동 일정으로 노출한다(FR-13~15). 신규 예약 메커니즘 없음, 정원은 Slot 소유(community 미관리). R1 준수 — booking import 금지, ACL 게이트웨이만.

포함 (전 레이어 수직 슬라이스, 전부 신규 파일):

  • domain: CommunityBooking(@Entity — communityId·slotId(Long ID참조)·linkedByUserId, create + 중복 링크 멱등 가드), CommunityBookingRepository interface, SlotInfoGateway interface + SlotInfo(community DTO: facilityId·date·timeRange·capacity).
  • domain service: CommunityBookingDomainServicelink(communityId, hostUserId, slotId)(CommunityDomainService.requireHost 위임 후 저장)·findLinked(communityId, requesterId)(requireActiveMember 인가 후 SlotInfoGateway로 표시정보 조합).
  • application: LinkCommunityBookingUseCase·ListCommunityBookingsUseCase + Command/Response.
  • infrastructure: CommunityBookingRepositoryImpl+JpaRepository, SlotInfoGatewayImpl(booking SlotRepository 읽어 SlotInfo 변환).
  • presentation: CommunityBookingApiController(POST/GET /communities/{communityId}/bookings) + @ConditionalOnProperty(community.booking.enabled).

주의: CommunityBookingDomainServiceCommunityDomainService를 주입해 인가를 위임(같은 도메인 내). booking은 SlotInfoGateway로만 접근.

롤백

community_bookings 테이블 신규 + 플래그. 롤백은 community.booking.enabled=false(경로 404) 또는 역방향 DDL(테이블 DROP).

의존

  • 없음 (기존 community·booking 재사용만)

다이어그램

처리 흐름

sequenceDiagram
    participant C as CommunityBookingApiController
    participant U as ListCommunityBookingsUseCase
    participant S as CommunityBookingDomainService
    participant Auth as CommunityDomainService
    participant G as SlotInfoGateway
    C->>U: list(communityId, requesterId)
    U->>S: findLinked(communityId, requesterId)
    S->>Auth: requireActiveMember(communityId, requesterId)
    S->>G: findBy(slotId)
    G-->>S: SlotInfo(시설·일시·정원)
    S-->>C: 목록

클래스 의존

flowchart LR
    CBDS[CommunityBookingDomainService] --> Repo[CommunityBookingRepository]
    CBDS --> Auth[CommunityDomainService]
    CBDS --> Gw[SlotInfoGateway]
    GwImpl[SlotInfoGatewayImpl] -.->|implements| Gw
    GwImpl --> SlotRepo[booking SlotRepository]

테스트 케이스

  • 방장이 예약 가능 slotId를 연결하면 CommunityBooking이 생성된다
  • 멤버가 연결 목록을 조회하면 SlotInfo(시설·일시·정원)가 함께 반환된다
  • 방장이 아닌 사용자의 연결 시도는 requireHost 예외(403)로 거부된다
  • PRIVATE 모임 비승인자가 연결 예약 상세를 열람하면 NotCommunityMemberException(403)이다
  • 연결 예약이 없는 모임 조회는 빈 목록을 정상 반환한다
  • 동일 slotId 중복 연결은 멱등하게 처리된다
  • 정원은 Slot의 capacity를 그대로 노출하고 community가 별도 관리하지 않는다