예제를 단순화 하기 위해 다음 기능은 구현X
Entity를 Repository를 통해 DB에 등록.
Service는 Repository를 통해 Entity에 접근하여 비즈니스 로직을 수행
MemberRepository
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager entityManager;
...
}
@PersistenceContext 어노테이션으로 EntityManager를 관리했다.
JPA를 통해 @PersistenceContext 어노테이션을 @Autowired 어노테이션으로도 관리해줄 수 있게 되었다.
@Autowired와 생성자로 대체 → @Autowired 생략 → @RequiredArgsContructor로 대체
많은 Repository가 필요했던 OrderService..
Service는 데이터를 사용하기 위해 DB에 실제적으로 접근하기 위한 Repository를 사용하며,
Repository는 실제 객체인 Entity를 다룬다.
여러번 보긴 했지만, 계속 신경 쓰자.
연간관계 주인 필드에서 주입하는 쪽으로 설계 할 것.
중복된 정보로 가입하는 경우를 막아야한다.
더 간단하게 멤버의 숫자만 세는것이 더욱 최적화 되는 경우가 많다.
최적화에 대해 생각해보자.
Order 클래스 내에 비즈니스 로직. 주문 생성 및 취소 조회등을 넣었다.
서비스 계층 은 단순히 엔티티에 필요한 요청을 위임하는 역할을 한다.
엔티티가 비즈니스 로직을 가지고 객체 지 향의 특성을 적극 활용하는 것을 도메인 모델 패턴이라고 한다.
반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분 의 비즈니스 로직을 처리하는 것을 트랜잭션 스크립트 패턴 이라고 한다.
트랜잭션 스크립트 패턴이 도메인 모델 패턴에 비해 치명적인 단점이 있다거나 하지는 않다.
실무에서 케바케로 잘 선택 해야 한다는 것..
Order의 경우 많은 연관관계가 걸려있어서 복잡한데, 변경사항이 있을때 유지보수가 쉽고 응집도가 높아져서 도메인 모델 패턴를 사용한다.
또 여기서 눈 여겨 볼것은 가변인자(varargs)가 사용 됬다는 것,
가변인자 파트를 확인하자 OrderItem은 몇개가 들어올지 모르기 때문.
Order 클래스의 조회로직.. 람다식에 대해 더 공부해야한다..........
생성자 차단
우리는 Order를 createOrder()
메서드를 통해 생성시키고자 한다.
하지만 잘 모르는 사용자, 개발자가 잘 모르고 기본 생성자를 통해 인스턴스를 생성하고 필드값을 Setter 를 통해 주입하려 할 수 있으니,
기본적으로 이러한 접근을 차단하는 것이 좋다.
설계는 폐쇄적일 수록 좋다..
@NoArgsConstructor(access = AccessLevel.PROTECTED)
를 통해 기본 생성자의 액세스 레벨을 protected로 외부에서의 액세스를 제한 해주자.
이는 OrderItem 클래스에도 적용될 것이다.
DB에 직접 영향을 주는 코드는 orderRepository.save(order);
한줄 뿐, 앞서 보았듯 Order의 연관관계 매핑된 Delivery와 OrderItem에는 cascade 옵션이 걸려있다.
이 옵션덕에 알아서 다 DB에 저장 되는것.
하지만 cascade를 어디까지 해야할지 고민해야한다.
order가 delivery와 orderitem을 관리한다.
참조하는 엔티티를 프라이빗하게 관리하는 오너일 경우에 달아주는게 좋다.
예를 들어 delivery를 다른 곳에서 많이 참조한다면, cascade를 사용하면 안된다.
OrderService.cancel()
public void cancelOrder(Long orderId) {
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
OrderService.cancle()
를 호출하면 도메인 모델 패턴을 통한 설계로 Order.cancle()
을 호출하게 된다.
jpa 진짜 강점.
주문 취소시 재고 변동에 대한 sql 쿼리를 날려줘야한다.
이것을 jpa가 모두 더티 체킹해서 업데이트 쿼리를 날려준다는 것.
Order.cancel()
public void cancel() {
if (delivery.getStatus() == DeliveryStatus.COMP) {
throw new IllegalStateException("배송 완료");
}
this.setStatus(OrderStatus.CANCEL);
for (OrderItem orderItem : orderItems) {
orderItem.cancel();
}
}
주문 취소 요청이 들어오면 이미 배송이 완료된 상태가 아닌지 체크하고,
OrderItem의 재고 수를 복원시켜줘야 하기때문에 모든 OrderItem에 대한 OrderItem.cancel()
메서드를 호출하고, 취소 상태로 만들어준다.
OrderItem.cancel()
public void cancel() {
getItem().addStock(count);
}
수량을 돌려준다.
위 로직을 잘 기억하자.