diff --git a/docs/quality/architecture-rules.md b/docs/quality/architecture-rules.md index c6c4d75..99a60b3 100644 --- a/docs/quality/architecture-rules.md +++ b/docs/quality/architecture-rules.md @@ -573,6 +573,73 @@ not to be equal to: "d14eb045@example.com" - ArchUnit baseline 291 → **241** (고아 baseline 1개 정리 포함) - 남은 241건 중 **237건이 도메인 경계** 한 규칙입니다 +## 남은 237건을 0 으로 만들지 않기로 했다 + +여덟 단계로 489 → 241 까지 왔고, 남은 241건 중 **237건이 "다른 도메인의 리포지토리를 직접 쓰지 +않는다" 한 규칙**입니다. 이 숫자를 0 으로 만드는 일은 다른 단계들과 성격이 다릅니다. 그래서 +무엇이 남았는지 먼저 쪼개 봤습니다. + +### 방향별 분포 + +| 방향 | 건수 | 성격 | +|------|------|------| +| admin → facility·policy·user·health | 87 | 어드민 화면의 **조회**(목록·상세) | +| notification → user | 29 | 알림을 사용자에게 **붙이기 위한** 엔티티 참조 | +| health → user | 27 | 같음 (건강기록·첨부) | +| community → user | 14 | 같음 (게시글·댓글 작성자) | +| policy → user | 13 | 배치가 대상자를 훑는다 | +| 나머지 (health→policy, chatbot→facility 등) | 67 | 추천·조회 조합 | + +### 왜 0 이 되지 않는가 + +엔티티 쪽을 세어 보면 답이 나옵니다. + +``` +23개 엔티티가 @ManyToOne User 를 들고 있다 +``` + +`Notification.user`, `HealthRecord.user`, `Post.author`, `Review.user` … 전부 **DB 외래키**입니다. +JPA 로 그 외래키를 채우려면 `User` 엔티티가 필요하고, 엔티티를 얻으려면 `UserRepository` 가 +필요합니다. 그래서 이 위반은 "정리하지 않은 코드" 가 아니라 **도메인 간 외래키를 JPA 연관으로 +표현한 결과**입니다. + +0 으로 만들려면 그 23개를 `Long userId` 로 바꿔야 합니다(4a 에서 `FacilityWaitlist.child` 를 +그렇게 바꿨습니다 — 읽는 곳이 없어서 값이 있었습니다). 하지만 나머지는 **읽습니다.** +알림 응답은 `notification.getUser().getUserId()` 를, 게시글 응답은 작성자 이름을 씁니다. +연관을 끊으면 그 자리마다 조회가 한 번씩 더 생기고, 목록 화면에서는 N+1 이 됩니다. +지금은 `join fetch` 와 배치 로딩으로 막고 있는 것들입니다. + +### 그래서 어디까지 하고 멈추는가 + +| 판단 | 근거 | +|------|------| +| **엔티티 연관 23개는 그대로 둔다** | 외래키가 있는 쪽이 사실이고, 끊으면 N+1 을 새로 만든다. 읽지 않는 연관이 발견되면 그때 개별로 바꾼다(4a 가 그 예다) | +| **어드민 조회 87건도 그대로 둔다** | 포트 4~5개를 만들어 `findAll` 을 중계하는 일인데, 어드민은 본래 전 도메인을 보는 화면이라 그 포트가 막는 사고가 없다. 6단계에서 서비스로 내려 트랜잭션 경계는 이미 제자리다 | +| **쓰기는 계속 입구로 모은다** | 4a(`ChildDirectory`)·6단계(`ModerationService.hidePost`)·#149(디스패처 한 곳)처럼, **같은 일을 두 방식으로 하는 것**만 고친다 | + +### 이 규칙을 왜 남겨 두는가 + +숫자를 0 으로 만들지 않더라도 baseline 은 계속 값을 합니다. **새 위반을 막기** 때문입니다 — +새 도메인이 남의 리포지토리를 주입하면 237 이 238 이 되고 빌드가 실패합니다. 지금 237 은 +"고쳐야 할 목록" 이 아니라 **"여기까지는 알고 있다" 는 선**입니다. + +### 여덟 단계에서 실제로 나온 것 + +숫자보다 이쪽이 중요합니다. 규칙을 맞추려고 코드를 옮기기 전에 **그 안을 먼저 읽은 것**이 +값을 했습니다. + +| 단계 | 찾은 것 | +|------|---------| +| 2 | "찾을 수 없습니다" 34곳이 **500** 이었다 (404 여야 한다) | +| 3a | 중복·거짓 엔드포인트 7개. 알림 설정은 **저장하지 않고** 받은 값을 돌려줬다 | +| 4a | 소유권 검증 3벌의 답이 **404·403·500** 으로 갈렸다. 403 은 자녀의 존재를 알려줬다 | +| 5b | 월령 계산 6벌 중 하나만 단위가 달라 **돌 지난 아이 전부에게 신생아 정책이 추천**됐다 | +| 5c | "일정" 이라는 API 두 개가 일정이 아니었다(권장 시기 항상 `null`) | +| 6 | 어드민 "알림 발송" 이 **발송하지 않았다**. 게시글 조치가 숨김/하드삭제로 갈렸다 | +| 7 | 관리자 탈퇴가 **개인정보를 남겼다**. 비로그인 전환 클릭이 **한 건도 집계되지 않았다** | +| 8 | 카카오 와이어 포맷이 우리 API 응답 패키지에 있었다 | +| (후속) | **탈퇴한 계정에 알림이 계속 발송됐다** (#149) | + ## 다음 단계 | 단계 | 내용 | 효과 |