[RFC] 엔티티 설계 전략 및 테스트 편의성에 관한 고찰 #7
Replies: 5 comments 1 reply
-
|
우석님 초기 전략에 대해서 잘 정리해주셔서 감사합니다. 의견에 앞서 현재 저희팀은 Spring Boot와 Java를 간결합한 구조를 선택했습니다. 이는 현 시점에서 우리 팀이 가장 잘 다룰 수 있는 기술 스택에 집중하여 생산성을 높이기 위한 결정이며, 이에 따라 저는 우석님 C의견에 동의하고 B의견을 변형해야한다고 생각합니다. 저희 서비스는 DDD를 지향하며, 주막 운영 시간 검증이나 게임 점수 계산 등 복잡한 비즈니스 로직이 엔티티 내부 행위 메서드로 많이 포함될 예정입니다.(풍부한 도메인이 될 것 이라고 생각 합니다!) 이 경우 단순 CRUD 테스트보다 엔티티 내부의 비즈니스 로직을 검증하는 테스트의 비중과 중요도가 높아질 것 입니다. 그러나 B의견 코드대로 구현한다면, id 필드에 대해서는 해결 방안그래서 저는 풍부한 도메인 모델로의 확장을 대비한 테스트 전략을 제안하고 싶습니다. B의견에 나오는 코드와 달리 테스트 코드에서만큼은 ID를 직접 제어할 수 있도록 열어두는 방식입니다. @Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Table(name = "notice")
public class Notice장단점장점
단점
하지만 이 단점도 추가적인 AI 리뷰 시스템이나 ArchUnit 같은 도구를 활용해, |
Beta Was this translation helpful? Give feedback.
-
|
A. 설계의 편의성 측면에서 객체 참조 방식이 좋다고 생각합니다. 단점을 보완하기 위해 PR 리뷰 단계에서 엔티티 추가 시 문제가 없는지 함께 고민해보고 머지하면 좋을 것 같습니다. B. 간결한 코드 유지를 위해 말씀해주신 5개 사항 모두 동의합니다. C. 테스트 코드 작성을 위해서 Builder에 id를 포함시키는게 좋다고 생각합니다. 인적 오류를 최소화하기 위해 테스트를 위한 엔티티 빌더에 createNoticeForTest와 같이 명시적으로 테스트를 위한 빌더임을 명시하면 좋을 것 같습니다. D. 앞서 언급한 C 내용과 더불어 정적 팩토리 메서드 도입 동의합니다. |
Beta Was this translation helpful? Give feedback.
-
|
A. 연관관계는 도메인 경계와 책임을 기준으로 객체 참조와 ID 참조를 구분하고 하는것이 바람직하다고 생각하지만 초기 단계에서 설계의 편의성과 도메인 모델의 의미를 명확히 구분해서 표현하기 위해 '객체 참조 방식'으로 구현하는 것이 좋을 것 같습니다. B. 제시해주신 Lombok 사용 방식은 전반적으로 합리적이며, 말씀해주신 방향으로 규칙을 정리하는 것이 바람직하다고 판단합니다. C. 저는 테스트 생산성과 가독성 측면에서 @builder에 ID 필드를 포함하는 방향에 찬성합니다. 하지만 운영 코드에서 id 주입 사고를 막는 가드레일이 필요합니다. ID 세팅은 테스트 전용 빌더로만 열어두는 방법으로 id를 세팅할 수 있는 빌더 API가 production 코드에서 보이지 않도록 분리하는 방식으로 예방책이 필요합니다. D. 정적 팩토리 메서드 도입은 개발자의 실수를 예방하고, 도메인 규칙을 강제하는 측면에서 매우 긍정적인 설계라고 생각합니다. |
Beta Was this translation helpful? Give feedback.
-
주요 논의 포인트 정리A. 객체 참조 vs ID 참조초기 단계에서 설계의 편의성과 도메인 모델의 의미를 명확히 표현하기 위해 객체 참조 방식을 채택함. B. Lombok 어노테이션 활용 범위
@Getter
@RequiredArgsConstructor
public enum NoticeType {
GENERAL("일반 공지"),
LOST_FOUND("분실물 공지");
private final String description;
}
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Table(name = "notice")
public class NoticeC. 테스트를 위한 ID 필드 노출 전략
D. 정적 팩토리 메서드(Static Factory Method) 도입개발자의 실수를 예방하고 도메인 규칙을 강제하기 위해 적용하는 것으로 확정함. 향후 계획ArchUnit이나 AI 리뷰 시스템 등의 도구를 활용해, src/main 아래 프로덕션 코드에서 빌더의 .id() 메서드가 호출되는 것을 차단하는 규칙을 도입하여 설계 안정성을 확보할 예정임.
|
Beta Was this translation helpful? Give feedback.
-
|
저는 객체 참조를 사용한다면, 연관 관계 주인쪽은 필드로 가지고, 주인이 아닌쪽은 연관 관계 편의 메서드를 정의하면서까지 아는 것은 불필요하다고 생각합니다. 예를 들어보겠습니다. 팀은 여러 멤버를 가지는 요구사항이라고 가정합니다. @Entity
class Member {
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
class Team {
// 연관관계 아예 없음
}Spring Data JPA를 사용하는 이유는 객체–관계 매핑의 복잡도를 줄이고, 도메인 중심으로 빠르게 개발하기 위함입니다. 도메인과 JPA를 결합해서 사용하는 상황에 행위 메서드가 많이 추가될 것인데, 편의 메서드는 도메인의 행위라고 볼 수 없고 RDB에 의존적인(인프라틱한) 코드라고 생각합니다. 따라서 객체 참조 방식을 사용하면, 단방향 연관관계를 우선으로 가져가고, 도메인 적으로 부분–전체 관계를 이룰 때에만 팀원들과 회의 후 양방향 연관관계를 도입하는 방식을 제안합니다. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
1. 배경
현재 공지사항(Notice) 도메인 개발을 진행하면서 엔티티 코드를 작성하고 있는 중입니다. 엔티티 코드를 작성하는 것은 단순하지만, 이러한 코드부터 모두가 공통된 규칙을 가지고 코드를 작성하면 추후 유지보수 하기에 용이할 것 같아 글을 작성하게 되었습니다.
2. 주요 논의 포인트
A. 객체 참조 vs ID 참조
1. 객체 참조
notice.getMember().getName()과 같이.연산자를 통해 연관 객체 정보를 직관적으로 가져올 수 있음.N+1 문제가 발생할 수 있으며, 의도치 않은 객체 로딩으로 인한 성능 저하가 발생할 수 있음.2. ID 참조
💡 질문
B. Lombok 어노테이션을 어느정도 사용할 것인지에 대해서
@Builder설정@Getter어노테이션을 사용.@NoArgsConstructor(access = AccessLevel.PROTECTED)를 통해 JPA 프록시 생성은 허용하되, 외부에서의 무분별한new호출은 차단.@Table어노테이션을 이용해 명시@Builder를 적용.@RequiredArgsConstructor활용Controller,Service등의 의존성 객체와Enum의 모든 필드를final로 선언하고, Lombok의@RequiredArgsConstructor를 사용하여 생성자를 자동 생성함.💡 질문
C. 테스트를 위한 ID 필드 노출 전략
@Builder에id필드를 포함하여, 테스트 코드 작성 시 DB 저장 없이도 특정 ID를 가진 Mock 객체를 생성할 수 있도록 설계를 제안합니다.ReflectionTestUtils와 같은 도구 없이도 깔끔한 테스트 코드 작성이 가능해짐.💡 질문
D. 정적 팩토리 메서드(Static Factory Method) 도입
createNotice()와 같은 의미 있는 이름을 가진 정적 메서드를 통해 객체 생성을 유도.💡 질문
3. 의견 요청
작성자: 유우석 (Backend Developer)
Beta Was this translation helpful? Give feedback.
All reactions