ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링부트/웹 애플리케이션 개발]주문 도메인 개발-1
    Spring&SpringBoot 2023. 1. 5. 12:02

    - 구현 기능 : 상품 주문, 주문 내역 조회, 주문 취소

    - 순서 : 주문 엔티티, 주문상품 엔티티 개발 > 주문 리포지토리 개발 > 주문 서비스 개발 > 주문 검색 기능 개발 > 주문 기능 테스트

     

     

     

    주문 엔티티, 주문상품 엔티티

    @Entity
    @Table(name = "orders")
    @Getter @Setter
    public class Order {
    
        //생성 메서드
        public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) {
            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() {
            if (delivery.getStatus() == DeliveryStatus.COMP) {
                throw new IllegalStateException("이미 배송 완료된 상품은 취소가 불가능합니다.");
            }
            this.setStatus(OrderStatus.CANCEL);
            for (OrderItem orderItem : orderItems) {
                orderItem.cancel();
            }
        }
    
        //비즈니스 로직, 전체 주문 가격 조회
        public int getTotalPrice() {
            int totalPrice = 0;
            for (OrderItem orderItem : orderItems) {
                totalPrice += orderItem.getTotalPrice();
            }
            return totalPrice;
        }
    
    }
    @Entity
    @Table(name = "order_item")
    @Getter @Setter
    public class OrderItem {
    
        //생성 메서드
        public static OrderItem createOrderItem(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() {
            getItem().addStock(count);
        }
    
        public int getTotalPrice() {
            return  getOrderPrice() * getCount();
        }
    }

     

     

     

    주문 리포지토리 

    package jpabook.jpashop.repository;
    
    import jpabook.jpashop.domain.Order;
    import lombok.RequiredArgsConstructor;
    import org.hibernate.type.OrderedSetType;
    import org.springframework.stereotype.Repository;
    
    import javax.persistence.EntityManager;
    
    @Repository
    @RequiredArgsConstructor
    public class OrderRepository {
        private final EntityManager em;
    
        public void save(Order order) {
            em.persist(order);
        }
    
        public Order findOne(Long id) {
            return em.find(Order.class, id);
        }
    }

     

     

     

    주문 서비스

    package jpabook.jpashop.service;
    
    import jpabook.jpashop.domain.Delivery;
    import jpabook.jpashop.domain.Member;
    import jpabook.jpashop.domain.Order;
    import jpabook.jpashop.domain.OrderItem;
    import jpabook.jpashop.domain.item.Item;
    import jpabook.jpashop.repository.ItemRepository;
    import jpabook.jpashop.repository.MemberRepository;
    import jpabook.jpashop.repository.OrderRepository;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    @Transactional(readOnly = true)
    @RequiredArgsConstructor
    public class OrderService {
    
        private final OrderRepository orderRepository;
        private final MemberRepository memberRepository;
        private final ItemRepository itemRepository;
    
        //주문
        @Transactional
        public Long order(Long memberId, Long itemId, int count) {
            //엔티티 조회
            Member member = memberRepository.findOne(memberId);
            Item item = itemRepository.findOne(itemId);
    
            //배송정보 생성
            Delivery delivery = new Delivery();
            delivery.setAddress(member.getAddress());
    
            //주문상품 생성
            OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
    
            //OrderItem orderItem = new OrderItem();
            //oderItem.setCount();
            //로직마다 생성을 다르게하면 유지보수가 어려움, 다른 스타일의 생성 막아야함
            //방법1. protected 이용 > orderItem에 코드 추가 : protected OrderItem() {}
            //방법2. lombok 이용 > @NoArgsConstructor(access = AccessLevel.PROTECTED)
    
            //주문 생성
            Order order = Order.createOrder(member, delivery, orderItem);
    
            //주문 저장
            //주문 저장할 때 cascade 옵션이 있기 때문에 orderitem, delivery 자동으로 함께 persist, db 인서트 됨
            orderRepository.save(order);
    
            return order.getId();
        }
        
        //취소
        @Transactional
        public void cancelOrder(Long orderId) {
            //주문 엔티티 조회
            Order order = orderRepository.findOne(orderId);
            
            //주문 취소
            order.cancel();
        }
    
        //검색
        //public List<Order> findOrder(OrderSearch orderSearch) {}
    
    }

    - 도메인 모델 패턴(http://martinfowler.com/eaaCatalog/ domainModel.html)

        > 주문 서비스의 주문과 주문 취소 메서드를 보면 비즈니스 로직 대부분이 엔티티에 있다.

        > 서비스 계층은 단순히 엔티티에 필요한 요청을 위임하는 역할을 한다.

        >> 이처럼 엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴

        >> 반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분의 비즈니스 로직을 처리하는 것을 트랜잭션 스크립트 패턴(http://martinfowler.com/ eaaCatalog/transactionScript.html)이라 한다.

        >> JPA의 경우 도메인 모델 패턴을 이용함

        >> 도메인 모델 패턴 vs 트랜잭션 스크립트 패턴? 뭐가 더 유지보수하기 쉬운지를 고려해보고 선택하면 됨

    728x90
Designed by Tistory.