[BE-03a] Room 컨텍스트 확장 (contextType/contextId)

작업 내용 (설계 의도)

변경 사항

근거 TDD: 20260704-채팅시스템고도화-tdd.md (FR-16 확장 메커니즘, 인터페이스 시그니처).

Room이 외부 도메인 엔티티에 연결될 수 있게 additive 확장한다. 기존 DIRECT/GROUP 방은 context null 유지.

  • Room.kt: contextType: RoomContextType?, contextId: Long? 필드 추가(nullable). 팩토리 createForContext(type, contextType, contextId, name) 추가. 질의 메서드 belongsToContext(): Boolean. 기존 createDirect/createGroup은 context null로 유지.
  • RoomRepository.kt + RoomCustomRepository.kt: findByContext(contextType, contextId): Room? 추가.
  • RoomRepositoryImpl.kt / RoomCustomRepositoryImpl.kt(infrastructure): QueryDSL로 findByContext 구현 (deleted_at IS NULL 필터).
  • 파일 범위: Room.kt, RoomRepository.kt, RoomCustomRepository.kt, RoomRepositoryImpl.kt, RoomCustomRepositoryImpl.kt만. RoomParticipant·MessageDomainService 무수정(BE-03b·BE-04와 파일 분리).
  • 롤백: 컬럼은 BE-01에서 nullable 추가라 코드 롤백만으로 안전.

의존

  • BE-01 (rooms.context_type/context_id 컬럼)
  • BE-02 (RoomContextType)

다이어그램

클래스 의존

flowchart LR
    Room --> RoomContextType
    RoomRepository --> Room
    RoomCustomRepository --> Room
    RoomRepositoryImpl -.implements.-> RoomRepository
    RoomCustomRepositoryImpl -.implements.-> RoomCustomRepository

테스트 케이스

  • Room.createForContext(GROUP, COMMUNITY, 10, "주말축구")는 contextType/contextId가 채워진 방을 만든다.
  • Room.createDirect()는 contextType/contextId가 모두 null이다 (기존 호환).
  • belongsToContext()는 context가 있으면 true, DIRECT 기본 방은 false다.
  • findByContext(COMMUNITY, 10)은 해당 컨텍스트 방 1건을 반환하고, 없으면 null이다.
  • soft-delete된 컨텍스트 방은 findByContext에서 제외된다.