10. 주문과 할인 도메인 개발
지난 포스트에서 설계한 주문과 할인 도메인을 바탕으로 개발을 진행한다.
https://debuggingworld.tistory.com/87
9. 주문과 할인 도메인 설계
이어서 비즈니스 요구사항에 따라 주문과 할인 도메인을 설계한다. 정의된 비즈니스 요구사항에 따르면, 주문과 할인 정책은 다음과 같다. https://debuggingworld.tistory.com/83 5. 비즈니스 요구사항과
debuggingworld.tistory.com
1. 주문과 할인 도메인
지난 포스트에서 설계한 주문과 할인 도메인 설계는 다음과 같다.
비즈니스 요구사항
- 주문과 할인 정책
- 회원은 상품을 주문할 수 있다.
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해줄 것. (나중에 변경 될 수 있다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루
- 고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)
도메인 협력 관계
클래스 다이어그램
객체 다이어그램
2. 주문과 할인 도메인 개발
1. 할인 정책 인터페이스와 구현체
core 패키지 하위에 discount 패키지를 생성하고, discount 패키지에 할인 정책 인터페이스와 구현체를 생성한다.
할인 정책 인터페이스 - DiscountPolicy.java
DiscountPolicy.java
package hdxian.hdxianspringcore.discount;
import hdxian.hdxianspringcore.member.Member;
public interface DiscountPolicy {
// 할인 액수 리턴
int discount(Member member, int price);
}
Member 객체와 상품 가격을 전달받고, 할인 액수를 리턴하는 discount() 메서드가 정의되어 있다.
할인 정책 구현체 - FixDiscountPolicy.java
비즈니스 요구사항에 따르면, 할인 정책은 회원 등급에 따라 고정 액수를 할인하는 방식이다.
DisCountPolicy의 구현체로 FixDiscountPolicy를 생성한다.
FixDiscountPolicy.java
package hdxian.hdxianspringcore.discount;
import hdxian.hdxianspringcore.member.Grade;
import hdxian.hdxianspringcore.member.Member;
public class FixDiscountPolicy implements DiscountPolicy {
// 할인 액수는 1000원
private int discountFixAmount = 1000;
@Override
public int discount(Member member, int price) {
if(member.getGrade() == Grade.VIP) {
return discountFixAmount;
}
else {
return 0;
}
}
}
전달받은 Member 객체의 등급을 확인하고, VIP 등급이면 할인 액수인 1000을, 아니면 0을 리턴한다.
2. 주문 도메인 개발
core 패키지 하위에 order 패키지를 생성한다.
도메인 협력관계에 따르면, 주문 서비스는 회원id, 상품명, 가격을 전달받고 주문 결과를 리턴한다.
먼저 주문 엔티티를 생성한다.
주문 엔티티 - Order.java
Order.java
package hdxian.hdxianspringcore.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;
}
// getter and setter
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 + '}';
}
}
주문 엔티티는 회원id, 상품명, 상품가격, 할인액수를 멤버로 가진다. 마치 영수증처럼 구성되었다.
getter와 setter를 가지고, 주문 내역을 확인할 수 있도록 toString()을 재정의(오버라이딩)하였다.
비즈니스 로직과 관련된 메서드는 calculatePrice() 메서드 하나로, 상품 가격에서 할인 액수를 뺀 최종 청구 가격을 계산해서 리턴한다.
주문 서비스 - OrderService.ajva
주문 서비스 인터페이스를 생성한다.
OrderService.java
package hdxian.hdxianspringcore.order;
public interface OrderService {
// 회원 id, 상품 이름, 가격을 전달받아 주문(Order)을 리턴함.
Order createOrder(Long memberId, String itemName, int itemPrice);
}
회원 id, 상품명, 가격을 전달받아 주문(Order)를 생성하는 createOrder() 메서드가 정의되어 있다.
주문 서비스 구현체 - OrderServiceImpl.java
주문 서비스 구현체를 생성한다. 설계상 유일한 구현체이므로 클래스명에 관례적으로 Impl을 붙인다.
OrderServiceImpl.java
package hdxian.hdxianspringcore.order;
import hdxian.hdxianspringcore.discount.DiscountPolicy;
import hdxian.hdxianspringcore.discount.FixDiscountPolicy;
import hdxian.hdxianspringcore.member.Member;
import hdxian.hdxianspringcore.member.MemberRepository;
import hdxian.hdxianspringcore.member.MemoryMemberRepository;
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);
// OrderService는 할인에 관여하지 않고 discountPolicy에 member정보를 넘기기만 한다.
// 단일 책임 원칙이 잘 지켜진 사례. 할인 정책이 변경되어도 OrderService는 변화가 없음.
int discountPrice = discountPolicy.discount(member, itemPrice); // discount()는 할인 액수를 리턴함.
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
도메인 협력 관계를 보면, 주문 서비스는 회원 저장소와 할인 정책에 의존성을 갖는다.
따라서 MemberRepository와 DiscountPolicy 참조변수를 멤버로 갖는다.
createOrder() 메서드는 정의된 것처럼 회원id, 상품명, 가격을 전달받아 주문을 생성한다.
눈여겨볼 점은 memberRepository를 통해 회원을 조회하고, discountPolicy를 통해 할인 액수를 계산한다는 점이다.
이는 객체 지향 프로그래밍에서의 단일 책임 원칙이 잘 지켜진 사례로, 회원 저장소나 할인 정책이 변경되어도 주문 서비스는 영향받지 않게 된다.
최종적으로 할인 액수가 계산되면 Order 객체를 생성해서 리턴한다.
주문과 할인 정책의 개발이 완료되었다. 다음 포스트에서는 이에 대한 테스트를 진행해본다.
테스트까지 하고나면 비즈니스 요구사항에 대한 모든 구현이 완료된다. 즉 프로그램이 완성된 것이다.