(Spring) Spring 없이 샘플 프로그램 구축하기 (회원사)

회원을 위한 간단한 예제 프로그램을 작성하려고 합니다. 프로그램을 작성하기 전에 요구 사항과 디자인을 살펴보겠습니다.

이 예제는 Spring을 사용하지 않고 순수한 Java 코드로 계속됩니다.

프로젝트 생성

먼저 Spring 프로젝트를 생성합니다.

요구 사항 및 디자인

회원

  • 회원은 로그인하여 문의하실 수 있습니다.
  • 회원등급은 일반회원과 VIP회원이 있습니다.
  • 구성원 데이터는 자체 데이터베이스를 구축하고 외부 시스템에 연결할 수 있습니다. (미확인)

주문 및 할인 정책

  • 회원은 상품을 주문할 수 있습니다.
  • 회원 등급에 따라 할인 정책이 적용될 수 있습니다.
  • 할인 정책은 모든 VIP에게 1,000원 ​​할인(변경 가능)을 제공하는 정액 할인을 적용합니다.
  • 할인 정책은 변경 될 수 있으며 개봉 직전까지 고민을 미루고 싶습니다. 최악의 경우 할인이 적용되지 않습니다. (미확인)

요구 사항을 살펴보면 미확인되거나 변경 될 수 있는 부분이 있습니다. 하지만 정책이 결정되기 전까지는 개발을 게을리 할 수 ​​없기 때문에 앞에서 배운 역할과 구현을 분리하는 방식으로 개발을 진행해 봅시다.

참고 :

더보기

Spring Boot를 사용하여 프로젝트 환경을 편리하게 설정하고 개발은 Spring 없이 순수한 Java 코드로만 수행됩니다.

멤버십 도메인 설계 및 개발(테스트)

회원 도메인 요구 사항

  • 회원가입, 신청가능
  • 회원등급은 일반회원과 VIP회원이 있습니다.
  • 멤버 데이터 자체 DB 구축 또는 외부 시스템 연동(미정)

멤버십 도메인 파트너십


클라이언트가 있으며 멤버십 서비스는 필요에 따라 가입 및 멤버십 요청 기능을 제공합니다. 가맹점은 불확실한 상태이므로 가능한 DB 가맹점, 외부 시스템 연결점 및 점포 가맹점을 생각하고 이를 위한 인터페이스로 가맹점을 구현할 수 있다.

멤버 클래스 다이어그램


멤버 개체 다이어그램


  • 회원 서비스: MemberServiceImpl

회원사

회원 등급

package hello.core.member;

public enum Grade {
 BASIC,
 VIP
}

회원사

package hello.core.member;

public class Member {
 private Long id;
 private String name;
 private Grade grade;
 
 public Member(Long id, String name, Grade grade) {
 this.id = id;
 this.name = name;
 this.grade = grade;
 }
 
 public Long getId() {
 	return id;
 }
 
 public void setId(Long id) {
 	this.id = id;
 }
 
 public String getName() {
 	return name;
 }
 
 public void setName(String name) {
 	this.name = name;
 }
 
 public Grade getGrade() {
 	return grade;
 }
 
 public void setGrade(Grade grade) {
 	this.grade = grade;
 }
}

가맹점

가맹점 인터페이스

package hello.core.member;

public interface MemberRepository {
 void save(Member member);
 Member findById(Long memberId);
}

스토리지 멤버 스토리지 구현

package hello.core.member;

import java.util.HashMap;
import java.util.Map;

public class MemoryMemberRepository implements MemberRepository {
 
 private static Map<Long, Member> store = new HashMap<>();
 @Override
 
 public void save(Member member) {
 	store.put(member.getId(), member);
 }
 
 @Override
 public Member findById(Long memberId) {
 	return store.get(memberId);
 }
}

데이터베이스가 자체 DB인지 외부 DB인지 아직 결정되지 않았으므로 먼저 메모리 멤버 저장소를 구현합니다.

회원 서비스

회원 서비스 인터페이스

package hello.core.member;

public interface MemberService {
 void join(Member member);
 Member findMember(Long memberId);
}

회원서비스 실시

package hello.core.member;

public class MemberServiceImpl implements MemberService {

 private final MemberRepository memberRepository = new MemoryMemberRepository();
 public void join(Member member) {
 	memberRepository.save(member);
 }
 
 public Member findMember(Long memberId) {
 	return memberRepository.findById(memberId);
 }
}

구성원 도메인 구현 및 테스트

회원 도메인 – 회원 등록 메인

package hello.core;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;

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 테스트를 사용해 봅시다.

멤버십 도메인 – 멤버십 테스트(JUnit)

package hello.core.member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

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

구성원 도메인 설계의 과제

public class MemberServiceImpl implements MemberService {

 private final MemberRepository memberRepository = new MemoryMemberRepository();

이 코드에서 MemberServiceImp는 MemberRepository 인터페이스와 그 구현인 MemoryMemberRepository에 의존합니다.

종속성이 인터페이스뿐만 아니라 구현에도 의존한다는 문제가 있습니다.

주문 및 할인 도메인 설계 및 개발(테스트)

주문 및 할인 정책

  • 회원은 상품을 주문할 수 있습니다.
  • 회원 등급에 따라 할인 정책이 적용될 수 있습니다.
  • 할인 정책은 고정 금액 리베이트를 적용하여 모든 VIP에게 1,000원 ​​할인(변경 가능 O)
  • 할인 정책은 변경될 수 있음(미정)

임무 영역 협업, 역할, 책임


1. 주문 생성 : 고객이 주문 서비스에 주문 생성을 요청합니다.

2. 회원 신청 : 할인을 위해서는 회원등급이 필요합니다. 따라서 순서 지정 서비스는 구성원 디렉토리에서 구성원을 검색합니다.

3. 할인 적용 : 주문 서비스는 회원 등급에 따라 할인 정책을 위임합니다.

4. 주문 결과 반송 : 주문 서비스는 할인 결과를 포함한 주문 결과를 반환합니다.

전체 작업 영역


역할수업 화신구현 객체를 자유롭게 조합할 수 있도록 분리 설계되어 있습니다.

예) 역할과 시행이 분리되어 있어 정액환급제도(1,000원 ​​고정)와 고정환율(10%)환급제도로 환원정책의 역할을 자유롭게 구성할 수 있다.

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


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


디스크 공간 정책 + 고정 요금 할인

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


DB 스토리지 + 백분율(%) 할인 정책

주문서비스는 회원이 매장에서 문의를 하여도 변경이 필요 없으며 정액할인정책(정액)을 지원하거나 DB가맹점+정액할인정책을 지원합니다. 협력적 역할 관계는 그대로 재사용할 수 있습니다.

주문 및 할인 도메인 개발

할인 정책 인터페이스

package hello.core.discount;
import hello.core.member.Member;

public interface DiscountPolicy {
 /**
 * @return 할인 대상 금액
 */
 int discount(Member member, int price);
}

정액할인제 실시

package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;

public class FixDiscountPolicy implements DiscountPolicy {
 private int discountFixAmount = 1000; //1000원 할인
 
 @Override
 public int discount(Member member, int price) {
 	if (member.getGrade() == Grade.VIP) {
 		return discountFixAmount;
 	} else {
 		return 0;
 	}
    }
}

VIP시 1,000원 ​​할인, 할인 없음!

작업 인스턴스

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 String getItemName() {
 	return itemName;
 }
 
 public int getItemPrice() {
 	return itemPrice;
 }
 
 public int getDiscountPrice() {
 	return discountPrice;
 }
 
 @Override
 public String toString() {
 	return "Order{" +
 		"memberId=" + memberId +
 		", itemName="" + itemName + "\'' +
 		", itemPrice=" + itemPrice +
 		", discountPrice=" + discountPrice +
 '}';
 }
}

주문 서비스 인터페이스

package hello.core.order;

public interface OrderService {
 Order createOrder(Long memberId, String itemName, int itemPrice);
}

주문 서비스 구현

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

주문 생성 요청 → 회원 정보 요청 → 할인 정책 적용 → 주문 상품 생성 및 반품

스토리지 구성원 저장소 및 FixDiscountPolicy를 구현으로 작성하십시오.

도메인 주문 및 할인 실행 및 테스트

주문 및 할인 정책 실행 – 가장 중요한 것

package hello.core;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.order.Order;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

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

주문 및 할인 정책 테스트 – JUnit 테스트

package hello.core.order;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

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