2.3 주문과 할인 도메인 설계 및 개발

주문과 할인 비즈니스 요구사항 설계

  • 주문과 할인 정책
    • 회원은 상품을 주문할 수 있다.
    • 회원 등급에 따라 할인 정책을 적용할 수 있다.
    • 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
    • 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루 고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)

주문과 할인 도메인 협력 관계

도메인 설계

Pasted image 20250313155205.png

  1. 주문 생성: 클라이언트는 주문 서비스에 주문 생성을 요청한다.
  2. 회원 조회: 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을 조회한다.
  3. 할인 적용: 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다.
  4. 주문 결과(객체) 반환: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.

역할과 구현을 분리했기 때문에 능동적으로 바꿀 수 있다.

(위에서 구현한 회원 저장소와 같이) Pasted image 20250313155459.png

객체 레벨로 구현

Pasted image 20250313155555.png

현재 주문 도메인의 객체 다이어그램

Pasted image 20250313155756.png

  • MemoryMemberRepository를 사용하고, 정액 할인 정책을 사용한 주문 도메인 -메모리 대신 DB회원 저장소를 쓰거나, 정률 할인정책을 지원하더라도 주문서비스의 역할을 변경할 필요가 없다.

주문과 할인 도메인 개발

Order객체

  • 이전 Generate 단축키로 생성자 및 getter / setter / ToString 삽입 Pasted image 20250313162400.png
package hello.core.order;  
  
public class Order {  
  
    private Long memberId;  
    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 int calculatePrice() {  
        return itemPrice - discountPrice;  
    }  
  
    public Long getMemberId() {  
        return memberId;  
    }  
  
...
  
    public void setDiscountPrice(int discountPrice) {  
        this.discountPrice = discountPrice;  
    }  
  
    @Override  
    public String toString() {  
        return "Order{" +  
                "memberId=" + memberId +  
                ", itemName='" + itemName + '\'' +  
                ", itemPrice=" + itemPrice +  
                ", discountPrice=" + discountPrice +  
                '}';  
    }  
}

OrderService 비즈니스 로직

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);  
        /* 해당 부분은 할인과 관련된 부분을 discountPolicy 에 위임하고 있기 때문에  
         단일 책임 원칙을 잘 지켰다고 볼 수 있다.*/  
        int discountPrice = discountPolicy.discount(member, itemPrice);  
          
        return new Order(memberId, itemName, itemPrice, discountPrice);  
    }  
}
  • 주문 생성 요청이 오면, 회원 정보를 조회 한 후애 할인정책에 회원정보를 단순히 넘겨준다. -> 단일책임 원칙이 잘 지켜진 것이다.

주문과 할인 도메인 테스트

OrderApp 작성 (only Java)

public class OrderApp {  
  
    public static void main(String[] args) {  
        MemberService memberService = new MemberServiceImpl();  
        OrderService orderService = new OrderServiceImpl();  
  
        Long memberId = 1L;  
        Member member = new Member(memberId, "memberA", Grade.VIP);  
        memberService.join(member);  
  
        Order order = orderService.createOrder(memberId, "itemA", 10000);  
  
        System.out.println("order = " + order);// order.toString 실행 (오버라이드)  
        System.out.println("order.calculatePrice = " + order.calculatePrice());
    }  
}

출력

order = Order{memberId=1, itemName='itemA', itemPrice=10000, discountPrice=1000}
order.calculatePrice = 9000 // 할인된 금액
BUILD SUCCESSFUL in 1s

JUnit 으로 테스트

package hello.core.member;  

import org.assertj.core.api.Assertions;  
import org.junit.jupiter.api.Test;  
  
public class OrderServiceTest {  
  
    MemberService memberService = new MemberServiceImpl();  
    OrderService orderService = new OrderServiceImpl();  
  
    @Test  
    void createOrder() {  
        Long memberId = 1L;  
        Member member = new Member(memberId, "memberA", Grade.VIP);  
        memberService.join(member);  
  
        Order order = orderService.createOrder(memberId, "itemA", 10000);  
        Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);  
    }  
}

출력

BUILD SUCCESSFUL in 1s

이전 멤버 도메인 서비스까지 함께 테스트

Pasted image 20250313165833.png

BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date
오후 4:58:55: Execution finished ':test --tests "hello.core.member.MemberServiceTest" --tests "hello.core.member.OrderServiceTest"'.

완료


할인정책이나, 저장소를 변경하였을 때 에도 객체지향적으로 잘 동작할까?