주문 도메인 개발

구현 기능

순서

주문 엔티티 개발

package jpabook.jpashop.domain;

import lombok.Getter;
import lombok.Setter;
import org.aspectj.weaver.ast.Or;

import javax.persistence.*;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static javax.persistence.FetchType.*;

@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {

    @Id @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    @ManyToOne (fetch = LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @OneToOne(fetch = LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "delivery_id")
    private Delivery delivery;

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private Orderstatus status; //주문상태 [ORDER, CANCEL]

    //연관관계 편의 메서드
    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }

    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }

    public void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }

    //생성 메서드 ...1
    public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) { //...2
        Order order = new Order();
        order.setMember(member);
        order.setDelivery(delivery);
        for (OrderItem orderItem : orderItems) {
            order.addOrderItem(orderItem);
        }
        order.setStatus(Orderstatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        return order;
    }

    //비즈니스 로직
    /**
     * 주문 취소
     */
    public void cancel() { //...3
        if (delivery.getStatus() == DeliveryStatus.COMP) {
            throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다.");
        }

        this.setStatus(Orderstatus.CANCEL);
        for (OrderItem orderItem : orderItems) {
            orderItem.cancel();
        }
    }

    //조회 로직
    /**
     * 전체 주문 가격 조회
     */
    public int getTotalPrice() { //...4
        int totalPrice = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getTotalPrice();
        }
        return totalPrice;
    }
}
  1. 주문이기 때문에 중요한게 있습니다. 생성 메서드를 만들어야합니다.

    주문 생성은 복잡합니다. Order만 생성해서 되는게 아니라 OrderItem, Delivery 등등 여러개 연관관계가 들어가고 복잡합니다. 그래서 이런 복잡한 생성은 별도의 생성 메서드가 있으면 좋습니다.

    정적 팩토리 메서드 (static factory method)

    이렇게 하면 Order가 연관관계를 쫙 걸면서 세팅이 되서 한 번에 생성이 됩니다.

    이게 중요한 이유는 앞으로 생성해야하는 지점을 변경해야한다면 생성 메서드만 변경하면 됩니다. 여기저기 찾아다닐 필요가 없습니다. 이게 정말 중요한 포인트입니다.

    <aside> 📖 [정리]

    밖에서 new해서 Order를 set, set, set 하는게 아니라 생성을 할 때부터 createOrder를 무조건 호출해야합니다. 그래서 관계가 있는 모든 값(member, delivery, status, orderDate)을 넣어서 생성 메서드에서 완결을 합니다.

    주문 생성에 대한 복잡한 비즈니스 로직을 다 응집해 놓는 것입니다.

    그래서 주문 생성에 관련된 로직은 createOrder에서만 고치면 됩니다.

    </aside>

  2. 가변인자(java varargs)

  3. 주문 취소

    1. 취소 버튼을 누르면 수량이 올라가야 합니다.

    2. this 생략

      for (OrderItem orderItem : this.orderItems) {           
      }
      

      this를 잘 안씁니다. 어차피 IDE가 색칠을 해줍니다. 그래서 굳이 쓸 의미가 없습니다.

      Untitled

      그래서 로직을 보면서 자기거라고 강조해야할 때나 이름이 똑같을 때 외에는 잘 안씁니다.

    3. OrderItem에도 다 cancel을 해줘야합니다.

      한 번 주문할 때 상품을 2번 주문할 수 있습니다. 그럼 OrderItem이 2개 생기니까 2개 각각 cancel을 날려줘야합니다.

      OrderItem의 cancel 메서드

    <aside> 📖 이미 배송된 상품은 주문하지 못한다는 비즈니스 로직에 대한 체크 로직이 엔티티 안에 있습니다.

    그 다음에 상태를 바꿔주고, 루프를 돌면서 OrderItem에 대해서 cancel을 치면 Item의 재고를 원복시켜줍니다.

    </aside>