프로젝트를 할 때마다 객체 생성과 그 위치에 관해 고민이 되어서 이번 기회에 정리해 보았다.
먼저 new 가 아닌 정적 팩토리 메서드와 빌더 패턴을 사용해서 호출 시에 매번 새로운 객체를 생성하지 않도록 구현하였다.
요청 DTO는 클라이언트로부터 입력받는 데이터를 표현하는 객체이다. 이 엔티티를 생성하는 로직은 두 가지 경우로 나뉜다.
정적 팩토리 메소드 사용
- 엔티티의 생성 로직이 복잡하거나 생성 방법이 자주 변경될 것으로 예상될 때
- 엔티티 내에 정적 팩토리 메서드를 정의 (객체 생성이기 때문에 DTO에 정적 팩토리 메소드를 정의하는 것보다 엔티티 내에 정의하는 것이 적절함)
- 엔티티의 생성 로직을 엔티티 내부에 캡슐화함으로써 엔티티의 생성 방법이 변경되더라도 서비스 계층의 코드 변경을 줄일 수 있다. 하지만 DTO가 엔티티의 생성 메서드에 의존하게 되어, 결합도가 높아질 수 있다.
toEntity 사용
- 생성 로직에 변화가 거의 없을 때
- 엔티티와 DTO의 결합도를 낮춰줄 수 있어 엔티티의 생성 로직이 변경되어도 DTO를 수정할 필요가 없어 엔티티와 DTO의 관심사를 분리할 수 있다.
따라서 엔티티를 생성하는 로직은 엔티티 클래스에 위치하는 것이 더 적절하다고 판단했다.
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderRequestDto {
@NotNull(message = "제품 아이디는 필수입니다.")
private Long productId;
public Orders toEntity(Member buyer, Member seller, Product product) {
return Orders.builder()
.orderPrice(product.getPrice())
.orderStatus(OrderStatus.거래시작)
.buyer(buyer)
.seller(seller)
.product(product)
.build();
}
}
응답 DTO는 서버가 클라이언트에게 보내는 데이터를 표현하는 객체이다. 따라서 엔티티의 데이터를 기반으로 생성되므로, 정적 팩토리 메소드를 응답 DTO 클래스 내부에 위치하도록하여, DTO 클래스가 데이터 표현의 책임을 가지게 된다.
이 때, 엔티티 내에 toResponse 메소드를 정의하여 직접 DTO로 변환하는 경우도 생각해보았는데, 이 경우는 엔티티와 DTO 간의 결합도가 증가하여 정적 팩토리 메소드를 사용하기로 결정했다.
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BuyerPurchaseHistoryResponseDto {
private Long orderId;
private int orderPrice;
private OrderStatus orderStatus;
public static BuyerPurchaseHistoryResponseDto from(Orders orders) {
return BuyerPurchaseHistoryResponseDto.builder()
.orderId(orders.getId())
.orderPrice(orders.getOrderPrice())
.orderStatus(orders.getOrderStatus())
.build();
}
}