| name | detoxmate-code-review |
| description | Detoxmate Spring Boot/JPA 변경 코드를 DDD, SOLID, Rich Domain Model, Aggregate 경계, JPA 사용 방식, 테스트 품질 관점으로 검토한다. GREEN 이후 리팩터링 검토 또는 "코드 리뷰", "설계 리뷰", "DDD 관점 리뷰", "JPA 관점 리뷰" 요청에서 사용한다. |
Code Review Skill - DDD / JPA / SOLID Focus
이 스킬은 Spring Boot + JPA 기반 백엔드 코드를 리뷰할 때 사용한다.
목표는 단순히 "코드가 동작하는지"를 보는 것이 아니라,
비즈니스 규칙과 상태 변화가 적절한 객체에 배치되어 있는지,
서비스가 과도한 책임을 지고 있지는 않은지,
JPA를 객체지향 모델링 도구로 잘 활용하고 있는지를 점검하는 것이다.
Review Philosophy
리뷰는 다음 원칙을 따른다.
- 단순 문법, 취향 차이, 사소한 스타일보다 설계와 책임 분리를 우선 본다.
- 비즈니스 규칙이 서비스 계층에 과도하게 밀려 있지 않은지 확인한다.
- 가능한 경우
anemic domain model보다 rich domain model에 가까운 방향을 제안한다.
- 다만 모든 로직을 엔티티에 몰아넣는 것은 지양한다.
- 도메인 객체, 애플리케이션 서비스, 인프라 계층의 책임 경계를 중요하게 본다.
- 패키지가 기술 계층보다 Detoxmate bounded context와 DDD 계층 책임을 드러내는지 확인한다.
- JPA를 단순 CRUD 도구처럼만 사용하지 않고, 객체 그래프와 변경 감지를 활용하고 있는지 살핀다.
- 리뷰는 비판보다 개선 방향 제시에 초점을 둔다.
Review Priorities
리뷰 우선순위는 아래와 같다.
- 책임 분리와 SOLID 원칙
- DDD 패키지 구조와 의존성 방향
- 도메인 모델의 풍부함 / 빈약한 도메인 모델 여부
- 서비스 계층 비대화 여부
- 엔티티가 FK 중심으로 흘러가는지 여부
- JPA 사용 방식의 적절성
- 테스트가 도메인 규칙을 잘 검증하는지
- 네이밍, 가독성, 중복 제거
Main Review Checklist
0. DDD 패키지 구조와 의존성 방향
docs/harness/detoxmate/ddd-package-structure.md를 기준으로 다음을 점검한다.
- 새 production class가
com.detoxmate.{domain} 아래의 업무 경계에 놓였는가
- 새 bounded context나 큰 기능 분리에서
domain, application, presentation, infrastructure 책임이 드러나는가
- 기존 작은 수정에서 요구사항과 무관한 대규모 패키지 이동을 하지 않았는가
- 최상위
controller, service, repository, dto, entity 패키지를 만들지 않았는가
presentation -> application -> domain, infrastructure -> domain/application 방향을 깨지 않았는가
domain이 Controller DTO, HTTP 타입, 외부 클라이언트, Redis/Kafka/S3/Firebase/Discord 같은 기술 구현을 직접 알지 않는가
common에 도메인 전용 상태, 예외, Validator, DTO, Repository, Service를 넣지 않았는가
리뷰 시 패키지 이동을 제안할 때는 현재 요구사항에 필요한 좁은 이동인지, 별도 리팩터링으로 분리해야 할 광범위한 이동인지 구분한다.
1. SOLID 원칙
다음을 점검한다.
- 하나의 클래스가 너무 많은 이유로 변경되지 않는가
- 서비스가 유스케이스 조율을 넘어 비즈니스 규칙까지 전부 떠안고 있지 않은가
- 도메인 로직이 적절한 객체로 이동 가능한가
- 인터페이스/구현 분리가 의미 있게 되어 있는가
- 확장에는 열려 있고 수정에는 닫히는 구조로 갈 수 있는가
- 의존 방향이 상위 정책에서 하위 구현으로 자연스럽게 흐르는가
특히 다음 문제를 주의 깊게 본다.
- 서비스 하나가 생성, 참여, 탈퇴, 검증, 저장 순서, 응답 조립까지 전부 담당하는 구조
- 서비스가 "모든 결정을 내리는 객체"가 되어버린 구조
- 상태 변경 규칙이 엔티티가 아니라 서비스 메서드 안 if/else에만 몰려 있는 구조
2. 빈약한 도메인 모델(anemic domain model) 여부
다음을 점검한다.
- 엔티티가 getter/setter와 필드 보관 역할만 하고 있지 않은가
- 핵심 규칙과 상태 변화가 엔티티/도메인 객체 안에 들어갈 수 있는데 서비스에만 남아 있지 않은가
- "누가 상태를 바꿀 수 있는지", "어떤 조건에서 상태가 바뀌는지"를 엔티티 스스로 설명할 수 있는가
특히 아래 신호를 문제로 본다.
- 엔티티가 데이터 컨테이너처럼만 동작한다
- 모든 검증이 서비스 계층에서만 이루어진다
- 도메인 규칙이 여러 서비스 메서드에 흩어진다
- 상태 변경 메서드 없이 setter 중심으로 값만 주입한다
3. 서비스 계층 비대화 여부
애플리케이션 서비스는 유스케이스를 조율해야 한다.
아래와 같은 경우 비대화 신호로 본다.
- 조회, 검증, 상태 변경, 저장, 응답 조립을 모두 한 메서드가 담당한다
- 서비스 메서드가 지나치게 길고 분기문이 많다
- 서비스가 여러 엔티티의 규칙까지 직접 판단한다
- 서비스가 엔티티 내부 규칙을 대신 설명한다
리뷰 시에는 단순히 "서비스가 길다"로 끝내지 말고,
다음 중 어떤 방향으로 분리 가능한지 제안한다.
- 엔티티로 이동 가능한 규칙
- 도메인 서비스로 분리할 규칙
- 애플리케이션 서비스에 남겨야 하는 조율 책임
- 인프라 계층으로 내려야 하는 기술 세부사항
4. FK 중심 설계 여부
다음을 점검한다.
- 서비스나 repository가
groupId, groupMemberId, groupChallengeId를 직접 조합하면서 흐름을 만드는가
- 객체 간 협력보다 ID 전달이 중심이 되고 있지는 않은가
- 엔티티 관계를 객체 참조보다 숫자 ID로만 다루는 코드가 많은가
특히 아래 패턴을 주의한다.
- 서비스가 여러 repository를 호출해 FK를 수동 조립한다
- 엔티티가 다른 엔티티와 협력하지 못하고 외부에서 연결된다
- 객체 모델보다 테이블 구조가 코드 흐름을 지배한다
5. Repository 설계
다음을 점검한다.
- repository가
table 1개 = repository 1개에만 머물러 있지 않은가
- repository가 도메인 관점의 조회/저장 단위가 아니라 단순 테이블 접근기로만 사용되지 않는가
- aggregate 경계를 고려하고 있는가
- 서비스가 repository 여러 개를 직접 엮으며 aggregate 일관성을 관리하고 있지는 않은가
6. JPA를 잘 사용하고 있는지
다음을 점검한다.
- JPA를 단순 ID 기반 CRUD 도구처럼만 사용하지 않는가
- 객체 그래프를 활용하고 있는가
- 연관관계와 변경 감지(dirty checking)를 적절히 활용하는가
- 서비스가 불필요하게 setter/저장 순서를 모두 관리하지는 않는가
- JPA가 제공하는 영속성 컨텍스트의 이점을 살리지 못하고 있지는 않은가
특히 아래 경우를 경계한다.
- 엔티티를 객체가 아니라 DB row처럼 다룬다
- 연관 객체를 두고도 ID만 넘겨서 다시 조회한다
- 변경 감지 대신 모든 필드를 수동 갱신한다
- 객체 그래프보다 저장 순서 제어에 지나치게 의존한다
7. 도메인 객체에 인프라 관심사가 섞이지 않는지
도메인 객체에는 다음과 같은 내용을 넣지 않는 방향을 권장한다.
ResponseStatusException
DataIntegrityViolationException
- HTTP 상태 코드
- 저장 재시도 로직
- 외부 API 호출
- 메시지 브로커, 락, 캐시, 트랜잭션 세부 구현
이런 것은 웹 계층, 애플리케이션 서비스, 인프라 계층이 책임져야 한다.
리뷰 시 도메인 로직을 엔티티로 옮기자고 제안하더라도,
인프라 관심사를 같이 밀어 넣는 식의 제안은 하지 않는다.
8. 테스트 품질
다음을 점검한다.
- 테스트가 단순 호출 순서 검증에만 치우치지 않는가
- 도메인 규칙을 검증하는 테스트가 있는가
- "누가 어떤 상태에서 어떤 행위를 하면 어떻게 바뀌는가"를 검증하는가
- 서비스 mocking 위주로만 구성되어 실제 규칙 검증이 약하지 않은가
좋은 테스트 방향은 다음과 같다.
- 엔티티/도메인 객체의 행위 테스트
- 유스케이스 단위의 서비스 테스트
- 인프라 의존성은 필요한 수준에서만 검증
Anti-Patterns To Flag
아래 패턴은 발견 시 우선적으로 지적한다.
- 엔티티가 getter/setter만 가진 데이터 컨테이너로 머무는 경우
- 서비스가 생성/참여/탈퇴/검증/저장/응답 조립까지 모두 담당하는 경우
- 상태 변경 규칙이 엔티티 밖 if/else에만 존재하는 경우
- FK 값 조합으로만 유스케이스가 흘러가는 경우
- 기술 계층명 중심의 최상위 패키지나 패키지만 DDD인 구조
- Domain 객체가 Controller DTO, HTTP, 외부 클라이언트, 메시징/캐시/스토리지 구현에 의존하는 경우
- repository가 테이블 접근기 이상의 역할을 못하는 경우
- JPA를 SQL 실행 대체 도구처럼만 사용하는 경우
- 도메인 객체가 웹/인프라 예외를 직접 던지는 경우
- 테스트가 호출 횟수와 mock 검증에만 치우친 경우
Output Format
리뷰 결과는 아래 형식으로 작성한다.
1. 총평
- 현재 코드의 전반적 인상
- 잘한 점 1~2개
- 가장 중요한 리스크 1~2개
2. 주요 리뷰 포인트
각 포인트마다 아래 구조를 따른다.
- 문제
- 왜 문제인지
- 현재 코드에서 보이는 징후
- 개선 방향
- 필요하면 간단한 예시
3. 우선순위
아래 레벨로 구분한다.
- Critical: 설계/정합성/유지보수성에 큰 영향을 주는 문제
- Important: 중기적으로 누적 비용이 커질 문제
- Suggestion: 지금 당장 치명적이지 않지만 개선하면 좋은 점
4. 마무리
- "지금 당장 바꾸면 좋은 1~3개"를 짧게 정리한다.
References
docs/harness/detoxmate/ddd-package-structure.md
.agents/skills/detoxmate-code-review/references/ddd-review.md
.agents/skills/detoxmate-code-review/references/jpa-review.md
.agents/skills/detoxmate-code-review/references/solid-review.md
Review Tone
- 공격적이거나 단정적인 표현을 피한다.
- "틀렸다"보다 "이 구조는 이런 비용을 만든다" 식으로 설명한다.
- 가능한 경우, 단순 비판보다 이동 가능한 책임의 방향을 함께 제안한다.
- 무조건 DDD를 강요하지 않는다.
- 현재 프로젝트 규모와 복잡도 대비 과한 설계를 요구하지 않는다.
- 다만 비즈니스 규칙이 계속 늘어날 가능성이 보이면, 더 풍부한 도메인 모델 방향을 제안한다.