Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions docs/quality/architecture-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |

## 다음 단계

| 단계 | 내용 | 효과 |
Expand Down
Loading