[Spring] 02-4. 주문과 할인 정책 도메인 설계 및 개발

지찬우·2022년 12월 21일
0

Spring

목록 보기
8/27
post-thumbnail

이 시리즈는 인프런 강의(김영한 님의 ‘스프링 핵심 원리 - 기본편’)로 공부하며 혼자 기록하고, 사람들과도 공유할 수 있도록 작성하는 글이다. 최대한 추가적인 정보는 공식 홈페이지, 문서를 보며 얻을 예정이다.
(개인적인 생각과 이해가 들어가 있기 때문에 저의 ‘무식함’이 있을 수 있습니다😜 혹시라도 이 글을 보게 되시는 분이 계시다면 잘못된 부분 댓글로 많이 알려주시면 너무 감사하겠습니다!!)

GitHub Repository : https://github.com/jcw1031/spring-core-study


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

지금까지 회원 도메인을 설계하고 개발한 후에 테스트까지 모두 해보았다. 이젠 회원 도메인보다 좀 더 복잡한 주문 • 할인 도메인을 설계하고 개발해 보자.

📃 주문 • 할인 도메인 설계

주문과 할인 정책에 관한 요구사항을 한 번 더 살펴보자.

[ 주문과 할인 정책 ]

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

주문 도메인 협력 • 역할 • 책임

회원 도메인을 설계할 때도 협력 관계 그림을 먼저 살펴봤었다. 이번에도 협력 관계 그림을 살펴보자.

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


예제를 간단하게 하기 위해 주문을 생성할 때는 회원의 id와 상품명, 상품 가격을 전달한다. 실무에서는 객체를 넘긴다고 한다.

실제로는 주문 데이터를 DB에 저장하겠지만, 예제가 너무 복잡해지므로 지금은 생략하고 단순히 주문 결과를 반환하는 방식으로 설계했다.

클라이언트는 main 또는 Spring MVC의 Controller 정도가 될 수 있다.

주문 도메인 전체

역할과 구현을 모두 표시한 그림이다.


역할과 구현을 분리하였기 때문에 자유롭게 구현 객체를 조립할 수 있게 설계되었다. 덕분에 회원 저장소는 물론, 할인 정책도 유연하게 변경이 가능하다.

주문 도메인 클래스 다이어그램

회원 도메인 때 설명했듯이, 정적인 표현이다. 클래스들만 분석하여 표현한 그림이다.


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

클래스 다이어그램과 다르게 동적으로 객체들의 연관관계가 맺어지는 그림이다.


저장소가 메모리 저장소에서 DB로 변경되고, 할인 정책이 정액 할인 정책에서 정률 할인 정책으로 변경되어도 주문 서비스를 변경할 필요가 없다. → 협력 관계를 그대로 재사용할 수 있다.

⁉️ 사실 앞에서는 분명 저장소를 변경하면 서비스에서도 변경이 발생하기 때문에 DIP를 지키지 못하고 있다고 했다. 나도 이 부분이 궁금했는데, 나와 같은 질문을 하신 분이 있어서 해당 질문글의 답변을 읽어보았다. 지금은 스프링을 사용하지 않기 때문에 DIP를 지키지 못하지만, 구조적으로는 서비스의 변경이 필요가 없다는 말 같다. 이 구조에서 스프링을 사용하면 가능하게 할 수 있을 것이니 강의를 계속 더 들어봐야 할 것 같다.

🛠️ 할인 도메인 개발

이제 설계를 마쳤으니, 주문 • 할인 도메인을 개발해 보자.

패키지 생성

src/main/java/Group이름.core 패키지 내에 discount 패키지를 생성한다.


할인 정책

할인 정책 인터페이스를 만들고 할인 정책 구현체를 만든다.

할인 정책 인터페이스

생성한 패키지 내에 할인 정책 인터페이스(DiscountPolicy)를 생성한다.


할인 정책 인터페이스에 discount() 추상 메서드를 선언한다. 매개변수는 Member 객체와 상품의 가격을 전달받는다. 리턴값은 할인 금액이다.

고정 할인인 경우에는 상품의 가격이 굳이 필요는 없지만, 나중에 정률 할인으로 변경하게 된다면 상품의 가격이 필요하기 때문에 상품 가격을 매개변수로 받는 것 같다.


할인 정책 구현체

할인 정책 인터페이스의 구현체인 고정 할인 정책 구현체를 생성한다.


할인 정책 인터페이스의 구현체이므로 implements DiscountPolicy를 추가해 주고, 추상 메서드를 구현하는 부분을 만든다.


요구사항에서 고정 할인 정책이라면 1000원을 할인하기로 했으니, 상수로 만들어준다.

강의에서는 그냥 변수로 만드셨지만, 상수로 만드는 게 맞다고 생각하였다.


할인 조건이 회원의 등급이 VIP인 경우에만 할인이 적용되므로, if 문을 사용하여 회원의 등급 조건을 확인하고 할인 금액을 리턴한다. VIP가 아닌 경우는 0을 리턴하면 된다. else는 굳이 필요 없지만 강의에서는 작성하셨기 때문에 똑같이 하도록 하겠다.


🛠️ 주문 도메인 개발

할인 도메인을 간단하게 모두 만들었다. 이제 주문 도메인을 만들어 본다.

패키지 생성

src/main/java/Group이름.core 패키지 내에 order 패키지를 생성한다.


주문 엔티티

order 패키지 내에 주문 엔티티 Order를 생성한다.


주문 엔티티는 다음과 같은 멤버 변수를 포함한다.

회원 id
상품 이름
상품 가격
할인 가격


회원 엔티티와 마찬가지로 생성자를 만들고 Getter/Setter도 만들어준다!(Generate 단축키 사용)


생성자와 Getter/Setter 사이에 메서드를 하나 추가하겠다. 바로 할인된 가격을 반환하는 메서드 calculatePrice()이다. 이 메서드는 간단하게 상품 가격에서 할인 가격을 뺀 금액을 리턴하면 된다.


추가적으로 toString() 메서드를 오버라이딩하여 주문 엔티티의 필드들을 쉽게 출력할 수 있도록 한다.

이 부분에 대해서는 뒤에서 설명한다고 한다. 아마 main에서 console 출력으로 비교할 때 보기 좋게 출력되도록 하려고 하시는 것 같다.


주문 서비스

주문 서비스 인터페이스를 만든 후 주문 서비스 구현체를 만들어 보겠다.

주문 서비스 인터페이스

order 패키지에 주문 서비스 인터페이스 OrderService를 생성한다.


OrderService에는 주문을 생성하는 createOrder() 메서드 하나만 있으면 된다. 추상 메서드를 선언한다. 매개변수는 회원 id, 상품 이름, 상품 가격을 전달받는다.


주문 서비스 구현체

주문 서비스 인터페이스를 구현할 주문 서비스 구현체 OrderServiceImpl을 order 패키지에 생성한다.


implements OrderService를 추가하고 추상 메서드를 구현하는 부분을 만든다.


주문 서비스 구현체 OrderServiceImpl은 회원 저장소와 할인 정책을 의존하기 때문에 다음과 같이 인터페이스의 인스턴스가 구현체 객체를 참조하도록 한다.


createOrder() 메서드에서는 Member 객체를 조회하여 가져오고, 그 Member 객체와 상품의 가격을 할인 정책에 전달해 준다. 리턴값이 할인 가격이므로 int형 변수로 받는다.

여기서 알 수 있는 것은 설계가 굉장히 잘 되어있다는 것이다. 왜냐하면 주문 서비스에서는 할인에 대해서는 모르기 때문에 할인 정챙게게 알아서 처리하고 결과만 던져달라고 하며 회원 객체와 상품 가격만 전달하고 있다. 할인에 대한 변경이 필요하면, 할인 정책만 수정하면 된다. SRP가 잘 지켜지고 있는 것이다‼️


마지막으로 Order 객체를 생성하여 리턴해주면 완성이다.


개발을 모두 마쳤으니, 주문과 할인 도메인 설계 때 협력 관계 그림에 나왔던 흐름과 한 번 맞춰보도록 하자.

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

  • 주문 서비스가 회원 저장소에서 회원을 조회한다 → MemberRepository의 findById()

  • 주문 서비스는 할인 여부를 할인 정책에 위임한다 → DiscountPolicy의 discount()

  • 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다 → return new Order(매개변수들)

이로써 설계한 대로 개발을 잘 한 것 같다!


다음 시간에는 주문과 할인 도메인을 테스트해 보도록 하겠다.

profile
좋은 개발자가 되자.

0개의 댓글