BE Study/Spring

회원 도메인 및 상품 할인 적용 판매

subengineer 2024. 6. 29. 11:24

스프링을 이해하기 앞서 자바만을 가지고 코드를 짜보면서 어떤 구조가 나오는 지 확인해볼 것이다.

 

다음과 같은 요구사항을 줬다고 가정해보자.

- 회원을 가입하고 조회할 수 있다.

- 회원은 일반과 VIP 두 가지 등급이 있다.

- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)

 

객체 지향에서 추상적 개념만 알고 있으면 실제 어떤 DB를 사용할 지 몰라도 상관 없으니 기본적인 설계를 짤 수 있다.

회원 클래스 다이어그램에서 정적으로 이론적인 움직임을 알 수 있고, 객체 다이어그램에서 실제 적용되는 구체적인 객체의 흐름을 나타냈다.

 

package hello.core.member;

public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

MemberService로부터 상속받은 MemberServiceImpl 구현코드이다. 순수 자바로만 짠 코드인데 객체지향요소 SOLID를 잘 지켰는지 봐야한다.

 

이 코드의 설계상 문제점은 무엇일까요?

다른 저장소로 변경할 때 OCP 원칙을 잘 준수할까요?

DIP를 잘 지키고 있을까요?

: 의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있음 주문까지 만들고나서 문제점과 해결 방안을 설명

 

memberRepository를 가져오는 부분을 보면 추상적 개념, 구체적 대상 모두 알고 있어야 함을 알 수 있다. 이는 OCP, DIP를 준수하지 못하는 코드였다.

 

주문과 회원 도메인 설계도 해보겠다.

 

주문과 할인 정책

- 회원은 상품을 주문할 수 있다.

- 회원 등급에 따라 할인 정책을 적용할 수 있다.

- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)

- 할인 정책은 변경 가능성이 높다.

- 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루 고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)

 

역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계했다. 덕분에 회원 저장소는 물론이고, 할인 정책도 유연하게 변경할 수 있다.

회원을 메모리에서 조회하고, 정액 할인 정책(고정 금액)을 지원해도 주문 서비스를 변경하지 않아도 된다. 역할들의 협력 관계를 그대로 재사용 할 수 있다.

 

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.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);
        int discountPrice = discountPolicy.discount(member, itemPrice);

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

 

OrderServiceImpl 구현 코드이다. discount는 discountPolicy가 혼자 책임을 맡아 SRP는 잘 지켜지고 있다.

하지만 Repository, DiscountPolicy를 정하는 과정에서 다형성은 잘 유지하고 있지만 여전히 구체적인 객체를 알고 있어야 하는 치명적 단점이 있다. 이는 나중에 클라이언트의 요구사항 변경 시 큰 Overhead가 필요할 것이다.

 

이에 대해 스스로 어떻게 해결할 지 고민해보고 스프링을 이해하면 좋을 것 같다.