도메인 주도 설계(DDD)를 채택한 프로젝트에서 엔티티의 생성 시점(createdAt)과 수정 시점(updatedAt)을 자동으로 관리하려면 어떤 방식이 더 적합할까? 특히, 도메인 계층과 데이터 계층이 분리되어 있고, merge를 통해 업데이트가 이루어질 가능성이 높다면 설계 선택에 신중해야 한다.
이번 글에서는 @PreUpdate와 @UpdateTimestamp를 비교하며, DDD 방식에 적합한 구현 방법을 제안해보겠다.
1. DDD 관점에서의 엔티티 상태 관리
DDD에서는 도메인 계층이 핵심이다. 따라서 엔티티의 상태(createdAt, updatedAt) 관리는 애그리거트 루트에서 책임지거나, 이를 데이터 계층에서 투명하게 처리할 수 있어야 한다.
이러한 요구사항을 충족하기 위해:
• 도메인 계층은 비즈니스 로직에 집중하고,
• createdAt, updatedAt과 같은 시간 정보는 데이터 계층에서 자동으로 관리되는 것이 이상적이다.
2. @PreUpdate와 DDD의 충돌 가능성
@PreUpdate는 JPA의 생명주기 이벤트에 의존한다. 그러나 DDD에서는 데이터 계층이 도메인 로직에 영향을 미쳐서는 안 된다. @PreUpdate를 사용하면 도메인 로직 외부에서 의도치 않게 엔티티 상태가 변경되는 경우가 발생할 수 있다.
특히, merge는 JPA의 준영속 엔티티를 영속 상태로 변경하는 과정에서만 @PreUpdate를 호출하므로, 모든 업데이트 시점이 보장되지 않을 가능성이 있다. 이는 DDD에서 일관된 상태 관리라는 원칙과 맞지 않는다.
3. @UpdateTimestamp와 DDD의 적합성
@UpdateTimestamp는 데이터 계층에서 Hibernate가 자동으로 필드 값을 갱신한다. 이는 다음과 같은 이유로 DDD와 더 잘 맞는다:
1. 명확한 역할 분리: 시간 관리 로직을 도메인 계층이 아닌 데이터 계층으로 위임.
2. 안정적인 동작: merge를 포함한 모든 상황에서 일관되게 updatedAt 필드를 갱신.
3. 간결한 코드: 생명주기 이벤트 메서드 없이 선언만으로 구현.
4. BaseEntity
아래는 DDD 원칙을 준수하면서 @UpdateTimestamp를 활용한 BaseEntity 설계 예시다:
package boongeo.halftime.common.infrastructure.entity;
import java.time.LocalDateTime;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class BaseEntity {
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
이 설계의 특징:
• createdAt: 엔티티가 처음 저장될 때 자동으로 설정.
• updatedAt: 데이터 계층에서 merge나 update 실행 시 자동 갱신.
• DDD 친화적: 도메인 계층에 비즈니스 로직을 집중시키고, 시간 관리 로직은 데이터 계층에 위임.
5. DDD 환경에서 시간 관리의 최적화
만약 시간 관리가 도메인 로직의 일부로 간주되어야 한다면, 별도의 도메인 서비스 또는 팩토리 메서드에서 이를 처리할 수도 있다. 그러나 대부분의 경우 시간 관리와 같은 인프라적인 부분은 데이터 계층에서 처리하는 것이 DDD의 의도와 더 잘 맞는다.
결론
DDD 방식을 채택한 프로젝트에서는 데이터 계층의 책임과 도메인 계층의 책임을 명확히 분리해야 한다. @UpdateTimestamp는 이 원칙에 충실하면서도 간단하고 안전한 해결책을 제공한다. 반면, @PreUpdate는 merge와의 동작 이슈 및 불필요한 의존성 문제로 인해 사용을 권장하지 않는다.
도메인 계층에 비즈니스 로직을 집중시키고, 시간 관리 같은 공통적인 작업은 데이터 계층에 맡겨, 더 명확하고 유지보수하기 쉬운 구조를 만들어 보자.
'코딩딩 > Spring' 카테고리의 다른 글
@Builder 사용에 대한 고찰 (0) | 2024.12.15 |
---|---|
의존성, 의존성 주입, 그리고 테스트 가능성(Testability) (0) | 2024.12.11 |
TDD와 BDD, 그리고 테스트 작성의 기본 개념들 (3) | 2024.12.11 |
테스트 코드, 어디까지 작성해야 할까? (5) | 2024.12.11 |
Mockito를 활용한 단위 테스트 정리 (3) | 2024.12.11 |