-
[스프링부트/웹 애플리케이션 개발]주문 도메인 개발-1Spring&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'Spring&SpringBoot' 카테고리의 다른 글
[스프링부트/웹 애플리케이션 개발]홈 화면과 레이아웃, 부트스트랩 적용, 회원등록 (1) 2023.01.05 [스프링부트/웹 애플리케이션 개발]주문 도메인 개발-2 (0) 2023.01.05 [스프링부트/웹 애플리케이션 개발]상품 엔티티 개발 (0) 2023.01.04 [스프링부트/웹 애플리케이션 개발]회원 기능 테스트 (0) 2023.01.04 [스프링부트/웹 애플리케이션 개발]회원 repository, 회원 service 개발, 인젝션 방법 3가지, @RequiredArgsConstructor (0) 2023.01.04