[BE-32] (후속·리팩토링) job_postings 애그리게이트화 — 본문·태그 cascade 영속화
상태: 후속 리팩토링 (test-green 전제). 기능 변경 없음. BE 컨벤션 정리(PR #42)에서
JobPostingDetailRepositoryImpl의 cascade 전환이 보류된 건의 후속.
배경 — 발견 경위
BE 컨벤션 정리(PR #42, no-business-flow-in-infra)에서 JobPostingMatchResultRepositoryImpl은 JPA @OneToMany(cascade, orphanRemoval) 애그리게이트로 전환해 save(root) 한 번에 자식까지 반영하도록 고쳤습니다. 그러나 JobPostingDetailRepositoryImpl(본문·구조화 태그 영속화)은 전환을 보류했습니다.
보류 사유: 본문(job_posting_descriptions)·태그(job_posting_source_tags)의 실제 부모는 매치 결과가 아니라 **job_postings**인데, 현재 JobPostingDetailRepository가 saveDescription·replaceSourceTags를 JobPosting 애그리게이트와 분리된 별도 경로로 다룹니다. 순진하게 cascade를 걸면 수집 델타 로직(본문 null 시 보존·태그 전량 교체)이 회귀할 위험이 있어, #42 범위에서 제외하고 후속으로 남겼습니다.
문제
JobPostingDetailRepositoryImpl의 saveDescription/replaceSourceTags가 JobPosting 저장과 별도 트랜잭션 단계로 호출됩니다(BE-10·BE-25 수집 델타가 오케스트레이션). 이는:
- 애그리게이트 경계가 흐려짐 — 본문·태그가 job_postings의 자식인데 독립 영속 경로를 가짐.
no-business-flow-in-infra원칙과 부분 상충 — 델타가 “본문 저장 → 태그 교체” 순서를 application/domain에서 관리(단, 48에서 태그 dedupe는 infra 매핑 단계 정규화로 정리됨).
해결책 (설계 시 검토)
JobPosting을 루트로 하는 애그리게이트로 재구조화:
JobPostingJpaEntity에@OneToMany(mappedBy="jobPosting", cascade=[ALL], orphanRemoval=true)로descriptions(0..1)·sourceTags(0..N) 매핑.JobPostingRepositoryImpl.save(jobPosting)한 번에 본문·태그까지 반영 —JobPostingDetailRepository의 별도 경로 제거 또는 흡수.- 수집 델타의 “본문 null 시 보존”(FR-11)은 도메인 규칙으로 올려, JobPosting 엔티티가 본문 갱신 여부를 자체 판단(infra가 아니라). 태그 전량 교체·dedupe도 애그리게이트 내부 규칙으로.
주의·전제
- 파괴적이지 않은 순수 리팩토링 — DB 스키마·API·동작 무변경(영속화 구조만). 기존 테스트(수집 델타·본문 보존·태그 dedupe #48·중복 방지)가 전부 green 유지되는 것이 완료 기준.
- BE-31(Testcontainers 싱글턴) 이후 진행 권장 — 이 리팩토링은 통합 테스트로 애그리게이트 저장을 검증해야 하는데, 현재 전체 스위트가 컨테이너 포화로 간헐 실패해 신뢰할 수 있는 green 확인이 어렵습니다.
- 회귀 위험(본문 보존·태그 교체 순서)이 있어 TDD로 기존 델타 시나리오를 먼저 고정한 뒤 구조 변경.
참고
- PR #42 —
JobPostingMatchResultRepositoryImplcascade 전환(선례),JobPostingDetailRepositoryImpl보류 기록 - PR #48 — 태그 dedupe(infra 매핑 정규화), 본문·태그 델타 로직 현행
- no-business-flow-in-infra 컨벤션
- BE-31 — 선행 권장(테스트 인프라 안정화)