월루를 꿈꾸는 대학생

[핵심 원리-기본]비즈니스 요구사항과 설계 본문

Programing/Spring Boot

[핵심 원리-기본]비즈니스 요구사항과 설계

하즈시 2023. 7. 18. 21:02
728x90

비즈니스 요구사항과 설계

회원

  • 회원을 가입하고 조회할 수 있다.
  • 회원은 일반과 VIP 두 가지 등급이 있다.
  • 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)

주문과 할인 정책

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

회원데이터 및 할인 정책은 아직 결정되지 않았기 때문에 무기한 대기일 수도 있다 그래서 인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계해야한다


회원 도메인 설계

회원 도메인 요구사항

  • 회원을 가입하고 조회할 수 있다.
  • 회원은 일반과 VIP 두 가지 등급이 있다.
  • 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)

회원 도메인 협력 관계

  • 클라이언트가 회원서비스를 조회 후 저장소를 호출
  • 저장소가 아직 안 정해졌으니까 인터페이스로
  • 기획자도 볼 수 있음
  • 클라이언트랑 서비스 저장소는 다 역할
  • 저장소 구현체를 3가지 정도 해두고 요구사항에 따라 끼우면 됨 !
    ![[Pasted image 20230710010520.png]]

회원 클래스 다이어그램

  • 멤버 서비스 역할을 인터페이스로 만듦
  • 저장소 역할 또한 인터페이스 만듦
  • 구현체는 2개
  • 개발자가 만듦
    ![[Pasted image 20230710010533.png]]

회원 객체 다이어그램

  • 클라이언트는 회원서비스 impl바라보고 회원서비스impl은 메모리 저장소
  • 회원서비스 = memverserviceImpl
  • 클라이언트가 실제 사용하는 인스턴스끼리의 참조
    ![[Pasted image 20230710010543.png]]

회원 클래스 다이어그램을 보고 만들면 된다


코드는 pdf참고

![[Pasted image 20230714011717.png]]

회원 엔티티

Member라는 기본 아이디, 이름, 등급을 가지는 클래스를 생성

회원 저장소

  • MemberRepositry : 인터페이스임 해당 리포지토리에서는 회원을 저장 , 조회 기능을 가짐
  • MemoryMemberRepository : 메모리에 데이터를 저장하는 구현체 , 데이터베이스가 정해지지 않아서 임시로 갈아끼울 구현체를 생성함

회원 서비스

  • MemberService : 멤버를 추가 , 멤버를 찾는 기능을 한다
  • MemberServiceImpl : 구현체임

회원 도메인 실행과 테스트

![[Pasted image 20230714012214.png]]
해당 흐름을 만든다

만들어 둔 거를 확인

public class MemberApp {  
    public static void main(String[] args) {  
        MemberService memberService = new MemberServiceImpl();  
        Member member = new Member(1L, "memberA", Grade.VIP);  
        memberService.join(member);  

        Member findMember = memberService.findMember(1L);  
        System.out.println("new member = : "+member.getName());  
        System.out.println("find member = : "+findMember.getName());  

    }  
}

위와 같이 확인해도 되지만 junit 이라는 걸 사용할 예정

테스트할 때 쓰는 패키지
import org.assertj.core.api.Assertions;

import org.assertj.core.api.Assertions;  
import org.junit.jupiter.api.Test;  

public class MemberServiceTest {  

    MemberService memberService = new MemberServiceImpl();  
    @Test  
    void join(){  
        //given  
        Member member = new Member(1L,"memberA",Grade.VIP);  
        //when  
        memberService.join(member);  
        Member findMember = memberService.findMember(1L);  
        //then  
        Assertions.assertThat(member).isEqualTo(findMember);  
    }  
}

회원 도메인 설계의 문제점

  • 다른 저장소로 변경할 때 OCP원칙 준수하는가?
  • DIP잘 지키고 있는가
  • *의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존 하는 문제점 *
public class MemberServiceImpl implements MemberService{  

    private final MemberRepository memberRepository = new MemoryMemberRepository();

MemberServiceImpl보면
MemberRepository 추상화에도 의존을 하고 있고
MemorytMemberRepository 구현체에도 의존을 하고 있다


주문과 할인 도메인

요구사항 확인

  • 회원 등급에 따라 할인 정책을 적용한다
  • 모든 vip는 1000원 할인 하는 고정금액 할인 적용 -> 할인 정책은 변경 가능성 높음
    ![[Pasted image 20230714014020.png]]
  1. 주문 생성: 클라이언트는 주문 서비스에 주문 생성을 요청한다.
  2. 회원 조회: 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을 조회
    한다. findById
  3. 할인 적용: 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다.
  4. 주문 결과 반환: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.

주문 도메인 전체

![[Pasted image 20230714014205.png]]

역할을 먼저 만들고 구현을 만든다
역할과 구현을 나눠서 만들어서 자유롭게 조립이 가능하고 유연하게 교체가능

클래스 다이어 그램

![[Pasted image 20230714014341.png]]

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

![[Pasted image 20230714014625.png]]

회원을 메모리에서 조회를 한 다음 정액 할인 정책을 지원해도 주문서비스 구현체는 변경 안 해도 됨
협력 관계를 그대로 재사용할 수 있다

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

![[Pasted image 20230714014737.png]]

이렇게 해도 주문 서비스를 변경하지 않아도 된다!
협력 관계 그대로 사용 가능

  1. 주문결과 반환 이 오더서비스
    public interface OrderService {  
     Order createOrder(Long memerId , 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 memerId, String itemName, int itemPrice) {  
        // 멤버를 찾고  
        Member member = memberRepository.findById(memerId);  
        // 설계가 잘 된 이유  
        // 오더서비스의 경우 할인에 대해서 잘 몰라! 할인에 대한 거는discountPolicy한테 오마카세함  
        // 단일체계원칙 잘 지킴 , 문제가 생기면 해당 부분만 고치면 됨  
        int dicountPrice = discountPolicy.discount(member,itemPrice);  
        // 할인된 주문 결과를 반환  
        return  new Order(memerId,itemName,itemPrice, dicountPrice);  
    }  
}

테스트 코드

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);  
    }  
}

지금까지 한 거

![[Pasted image 20230714021104.png]]

여기서 정액할인과 메모리 회원 저장소를 사용

다음 챕터는 다형성을 활용해서 다른 구현체를 끼워보고 깔끔하게 되는지 다형성을 잘 활용했는지 확인할 것!

 

출처

https://inf.run/wFfL

 

728x90