주문 엔티티

PPakSSam·2022년 2월 11일
0
post-thumbnail
post-custom-banner

주문 엔티티


@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "orders")
@Entity
public class Order {
    public static final String CANT_CANCEL_MESSAGE 
                                    = "이미 배송완료된 상품은 취소가 불가능합니다.";

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

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

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

    @OneToOne(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    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) {
        this.orderItems.add(orderItem);
        orderItem.setOrder(this);
    }

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

    // 생성 메소드
    public static Order of(Member member, Delivery delivery,
                           OrderItem... orderItems) {
        Order order = new Order();
        
        order.setMember(member);
        order.setDelivery(delivery);
        order.orderItems.addAll(Arrays.asList(orderItems));
        order.setStatus(OrderStatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        
        return order;
    }

    // 비즈니스 로직
    public void cancel() {
        if(this.delivery.getStatus() == DeliveryStatus.COMP) {
            throw new IllegalStateException(CANT_CANCEL_MESSAGE);
        }

        this.status = OrderStatus.CANCEL;
        this.orderItems.forEach(OrderItem::cancel);
    }

    // 조회 로직
    public int getPriceTotal() {
        /*int priceTotal = 0;
        for (OrderItem orderItem : orderItems) {
            priceTotal += orderItem.getTotalPrice();
        }*/
        return this.orderItems.stream().mapToInt(OrderItem::getPriceTotal).sum();
    }
}

연관관계 메소드에 대해 신경쓰자

DB 관점에서는 연관관계 메소드는 굳이 필요하지 않습니다.
왜냐하면 두 테이블 중 하나에만 외래키를 넣어준다면 두 테이블은 연결되기 때문입니다.

하지만 자바에서는 얘기가 달라집니다.
자바에서는 외래키 개념이 없습니다. 따라서 두 객체가 양방향 연관관계라면 두 객체 모두에게 값을 넣어줘야 합니다. 그래서 연관관계 메소드가 필요합니다.

DDD

상품 엔티티에 대해서도 언급했지만 DDD는 도메인에서 비즈니스 로직을 처리한 것을 말합니다. 보통 서비스 레이어에서 비즈니스 로직을 처리하지만 그렇게 코드를 짜면 객체지향적이기 보단 절차지향적으로 코드를 짤 가능성이 높습니다. 그런데 도메인에서 비즈니스 로직을 처리하면 서비스 레이어에서는 객체에게 메세지를 보내는 식으로 코드를 짜게 되므로 객체지향적으로 짤 가능성이 높습니다.

주문 상품 엔티티


@Entity
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    private int orderPrice; // 주문 가격
    private int count; // 주문 수량

    // 생성 메소드
    public static OrderItem of(Item item, int orderPrice, int count) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setOrderPrice(orderPrice);
        orderItem.setCount(count);

        item.removeStock(count);
        return orderItem;
    }

    // 비즈니스 로직
    public void cancel() {
        this.item.addStock(this.count);
    }

    // 조회 로직
    public int getPriceTotal() {
        return this.orderPrice * this.count;
    }

}

생성 메소드에서 item.removeStock(count) 부분을 주의 깊게 봐주시길 바랍니다.
주문 상품 객체를 생성하면 상품의 재고는 당연히 줄어들게 됩니다. 이를 생성메소드에 반영한 것입니다. 보통 생성메소드라고 하면 단순히 객체를 생성하는 것만 생각하는 경우가 많은데 (저도 그랬습니다 ㅎㅎ) 이렇게 비즈니스 로직이 들어가도 됩니다.

cancel 메소드에 대해서도 생각해볼 여지가 있는 것 같습니다.
주문 상품 객체가 생성되면 상품 객체의 재고는 줄어들게 됩니다. 그런데 취소를 하면 주문 상품의 수량만큼 재고가 복구될 것입니다. 이 로직을 반영한 것입니다.

profile
성장에 대한 경험을 공유하고픈 자발적 경험주의자
post-custom-banner

0개의 댓글