주문과 할인 도메인 설계

2023. 7. 6. 23:00Spring/[inflearn]스프링 핵심 원리 - 기본편

◎ 비즈니스 요구 사항

<주문과 할인 정책>

- 회원은 상품을 주문한다.

- 회원 등급에 따라 등급에 해당하는 할인을 적용받을 수 있다.

- 모든 VIP등급의 회원은 1000원을 할인해준다.(임시 정책으로 이후에 변경 가능성이 있다.)

 

◎ 주문 도메인 설계

-> 위 도메인 설계를 기반으로 아래와 같이 인터페이스와 클래스를 구현한다.

 

◎ 주문 도메인 개발

-> 할인 정책 인터페이스

/**
 * <할인 정책 인터페이스>
 */
public interface DiscountPolicy {
    int discount(Member member, int price); // 할인 대상 금액 리턴
}

-> 정액 할인 정책(할인 정책 인터페이스의 구현 클래스)

/**
 * <정액 할인 정책>
 * 회원등급이 VIP인 회원에 대해 1000원 할인
 */
public class FixDiscountPolicy implements DiscountPolicy{
    private final int discountFixAmount = 1000; // 할인 금액 : 1000원
    @Override
    public int discount(Member member, int price) {
        if (member.getGrade() == Grade.VIP) {
            return discountFixAmount; // 회원 등급이 VIP면 1000원 할인 적용
        }else{
            return 0;
        }
    }
}

 

-> 주문 엔티티 클래스

/**
 * <주문 엔티티>
 */
public class Order {
    private Long memberId; // 주문한 회원의 id
    private String itemName; // 주문 상품 이름
    private int itemPrice; // 상품 가격
    private int discountPrice;  // 할인 가격

    public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
        this.memberId = memberId;
        this.itemName = itemName;
        this.itemPrice = itemPrice;
        this.discountPrice = discountPrice;
    }

    public Long getMemberId() {
        return memberId;
    }

    public void setMemberId(Long memberId) {
        this.memberId = memberId;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public int getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(int itemPrice) {
        this.itemPrice = itemPrice;
    }

    public int getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(int discountPrice) {
        this.discountPrice = discountPrice;
    }

    @Override
    public String toString() {
        return "Order{" +
                "memberId=" + memberId +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", discountPrice=" + discountPrice +
                '}';
    }
}

 

-> 주문 서비스 인터페이스

/**
 * <주문 서비스 인터페이스>
 */
public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice); // 주문 생성
}

 

-> 주문 서비스 구현 클래스

/**
 * <주문 서비스 구현 클래스>
 */
public class OrderServiceImpl implements OrderService{

    //회원 저장소와 할인 정책을 의존할 때 각 인터페이스의 구현 클래스에 의존한다.
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

- 현재 구현된 주문 서비스 구현 클래스에서 구현 클래스인 MemoryMemberRepository와 FixDiscountPolicy를 의존하고 있다.

- 위 코드처럼 구현을 하면 회원 정보를 저장하는 방식을 바꾸거나 할인 정책이 바뀌게 되면 해당 코드를 직접 변경해야 한다.

- 위 코드는 DIP와 OCP를 모두 위반하였다.

- 이 문제는 주문 관련 코드까지 만들고 나서 이후에 수정할 계획이다.

 

-> 주문 및 할인 정책 적용 테스트

/**
 * <주문 및 할인 정책 테스트>
 */
public class OrderServiceTest {
    MemberService memberService = new MemberServiceImpl();
    OrderService orderService = new OrderServiceImpl();

    @Test
    @DisplayName("주문 및 할인 정책 테스트")
    void createOrder(){
        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP); // 등급인 VIP인 memberA라는 회원
        memberService.join(member); // 회원 가입

        Order order = orderService.createOrder(memberId, "itemA", 10000); // 주문 생성
        Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000); // 할인된 금액이 1000원이 맞는지 검증
    }
}

- 회원 등급이 VIP인 회원이 물건을 주문했을 때 1000이 할인되는지 검증한다.

 

-> 결과

- 테스트에 통과했다. 작성한 로직이 정상적으로 동작한 것을 알 수 있다.

 

☆ 참고

[인프런]스프링 핵심 원리 - 기본편