도메인 모델과 JPA 엔티티 분리 설계 - KimGyuBek/Threadly GitHub Wiki
Threadly는 비즈니스 규칙 중심의 도메인 모델과 데이터 저장 중심의 JPA Entity를 명확히 문리했다.
이 설계는 비즈니스 로직의 순수성을 유지하면서, 영속성 계층의 성능과 유연성을 동시에 확보하기 위함이다.
- 도메인 순수성 유지: JPA 제약(
@Entity등)에 대한 의존성을 가지지 않는다. - 비즈니스 의미 강화: 도메이은 행위 중심으로, Entity는 저장 구조 중심으로 설계한다.
- 테스트 용이성: DB 의존 없이도 도메인 단위 테스트 가능하다.
- 책임 분리: 도메인 변경과 DB 구조 변경을 독립적으로 관리한다.
- 외부 기술에 대한 의존성을 가지지 않는다.
- 오직 비즈니스 규칙과 상태 표현만 담당하며, 프레임워크나 DB와는 완전히 분리된다.
- 연관관계 매핑, 컬럼 제약 등은 Persistence 전용 역할이다.
- 도메인과 별개로 DB 구조 및 성능에 맞게 최적화된다.
- 데이터 무결성, 조회 성능, 저장 효율성을 보장하는 것이 목적이다.
- 도메인 모델에는 비즈니스 로직 수행에 필요한 필드만 포함한다.
- 엔티티는 데이터 저장에 필요한 필드만 포함한다.
- 두 모델은 목적이 다르므로 구조가 일치할 필요가 없다.
- 저장에 최적화된 필드와 도메인 로직에 필요한 필드가 상이하기 떄문에 별도의 매핑 과정을 통해
Domain->Entity변환이 필요하다.
- 서비스계층에서 도메인 로직 수행 후, DB와의 상호작용이 필요한 시점에서
Mapper가 변환을 전담한다. - 도메인은 JPA를 모르며, 외부 기술에 의존하지 않는다.
관련 문서: 도메인/엔티티 매핑 전략
예시:
//Follow.java
@Getter
@AllArgsConstructor
@Builder
public class Follow { // Follow 도메인
private String followId;
private String followerId;
private String followingId;
private FollowStatus statusType;
/*도메인 로직 생략*/
}//FollowEntity.java
@Table(name = "user_follows")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class FollowEntity extends BaseEntity {
@Id
@Column(name = "follow_id")
private String followId;
@JoinColumn(name = "follower_id")
@ManyToOne(fetch = FetchType.LAZY)
private UserEntity follower;
@JoinColumn(name = "following_id")
@ManyToOne(fetch = FetchType.LAZY)
private UserEntity following;
@Column(name = "status")
@Enumerated(EnumType.STRING)
private FollowStatus statusType;
}