주문 도메인 개발

구현 기능

순서

주문 엔티티 개발

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>