본문 바로가기

토이프로젝트

주문하기 (1)

먼저 주문쪽 Entity를 조금 수정했다.

Order와 OrderDetail 관계는 일반적인 업무 로직이 Order를 중심으로 OrderDetail이 생성되거나 수정되기 때문에 ManyToOne 보다는 OneToMany로 변경하였고 이를 효율적으로 관리하기 위해 양방향 연관관계를 통해 mappedBy를 통해 FK를 OrderDetail로 내렸다.

(이렇게 하지 않으면 OneToMany로 동시 Insert 시 불필요한 Update 쿼리가 날라갈 수 있음)

 

추가로 Order Entity 내에 addOrderDetail()와 createOrder() 를 통한 도메인 로직을 추가하였고, orderStatusCode는 Enum Class를 활용하였다.

@Entity
@Table(name = "TB_ORDER")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order extends BaseEntity {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY) // 데이터 베이스에 위임
	private Long id;
	
	private LocalDateTime orderDate;
	
	private Long customerId;
	
	private BigDecimal totalPrice;
	
	@Enumerated(EnumType.STRING)
	private OrderStatus orderStatusCode;
	
	@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private List<OrderDetail> orderDetailList = new ArrayList<>();

	@Builder
	private Order(Long id, LocalDateTime orderDate, Long customerId, BigDecimal totalPrice, OrderStatus orderStatusCode) {
		this.id = id;
		this.orderDate = orderDate;
		this.customerId = customerId;
		this.totalPrice = totalPrice;
		this.orderStatusCode = orderStatusCode;
	}
	
	public void addOrderDetail(OrderProdDto orderProdDto) {
		
		OrderDetail orderDetail  = OrderDetail.builder()
        					.order(this)	// 양방향 객체 매핑
        					.prodId(orderProdDto.getProdId())
        					.orderProdPrice(orderProdDto.getOrderProdPrice())
       						.orderProdQuantity(orderProdDto.getOrderProdQuantity())
        					.build();
		orderDetailList.add(orderDetail);
	}
	
	public void createOrder() {
		this.orderDate = LocalDateTime.now();
		this.orderStatusCode = OrderStatus.ORDER_RECEIPT;
		
		// orderDetailList에 포함된 금액의 총합을 계산한다.
		this.totalPrice = orderDetailList.stream().map(dto -> dto.getOrderProdPrice()).reduce(BigDecimal.ZERO, BigDecimal::add);
	}
	
}

중요한 포인트는 addOrderDetail()에서 .order(this) 를 통해 양방향 객체 매핑을 처리한 부분과 createOrder()를 통해 객체 생성 시 필요한 설정(주문 일시, 상태 코드)을 처리한 부분이다.

 

Enum Class는 공통 Domain 프로젝트로 분리하여 처리 하였다.

public enum OrderStatus {

	ORDER_RECEIPT,
	ORDER_COMPLETED,
	ORDER_FAILURE_OUT_OF_STOCK,
	ORDER_FAILURE_INSUFFICIENT_BALANCE;
	
}

서비스 로직은 다음과 같다.

@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {
	
	private final OrderRepository orderRepository;
	
	public Long createOrder(OrderCreateRequestDto orderCreateRequestDto) {
		
		Order order = orderCreateRequestDto.toEntity();
		
		// 생성 초기값 셋팅
		order.createOrder();
		
		orderRepository.save(order);
		
		return order.getId();
	}
}
@Data
public class OrderCreateRequestDto {
	
	private Long customerId;
	
	private List<OrderProdDto> orderProdDtoList;
	
	public Order toEntity() {
		Order order = Order.builder()
                      		.customerId(customerId)
                      		.build();
		
		// 상품 상세 정보 List를 Entity의 add 기능을 통해 전달한다.
		orderProdDtoList.stream().forEach(dto -> order.addOrderDetail(dto));
		
		return order;
	}

}

 

동작 확인을 위해 테스트 코드를 구현하여 검증해 본다.

@SpringBootTest
@Transactional
@Rollback(true)
public class OrderRepositoryTest {
	
	@Autowired
	private OrderRepository orderRepository;
	
	@PersistenceContext
	private EntityManager em;
	
	@Test
	public void testOrder01() {
		
		// Given
		Order order = Order.builder()
                              		.customerId(1L)
                              		.build();
		
		order.addOrderDetail(new OrderProdDto(10L, 1, new BigDecimal(3000)));
		order.addOrderDetail(new OrderProdDto(11L, 2, new BigDecimal(7000)));
		
		order.createOrder();
		
		// When
		orderRepository.save(order);
		
		em.flush();
		em.clear();
		
		Order selectOrder = orderRepository.findById(order.getId()).get();
		
		// Then
		assertThat(order.getId()).isEqualTo(selectOrder.getId());
		assertThat(order.getCustomerId()).isEqualTo(selectOrder.getCustomerId());
		assertThat(order.getOrderDetailList().size()).isEqualTo(selectOrder.getOrderDetailList().size());
	}

}

영속성을 Clear 시킨 후 실제 DB에 저장된 결과를 조회하기 위해 em으로 flush, clear를 진행 후 비교 하였다.

 

정상적으로 테스트가 동작됨을 확인할 수 있다.

 

다음 포스팅에서는 실제 주문을 위해 User Entity의 잔액확인 및 차감, 재고 확인 및 차감 등의 기능을 비동기 메시지 방식을 통해 처리하는 과정을 진행하려 한다.

'토이프로젝트' 카테고리의 다른 글

프로젝트 생성  (0) 2021.04.11
토이프로젝트 시작  (0) 2021.04.08