Spring Basic #. 예제 비즈니스 요구사항과 설계
1. 비즈니스 요구사항과 설계
먼저, 예제 코드를 작성하기 전에 다이어그램으로 비즈니스 요구사항과 설계를 해보도록 하겠다.
코드 작성을 시작하기에 앞서서 비즈니스 요구사항을 먼저 파악한 후 다이어그램 등으로 설계를 하면 조금 더 체계적인 프로그래밍을 할 수 있다.
- 회원
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다.(미확정)
- 주문과 할인 정책
- 회원은 상품을 주문할 수 있다.
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라.(나중에 변경 될 수 있다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다.(미확정)
요구사항만 보자면 회원 데이터, 할인 정책 같은 부분은 지금 결정하기 어려운 부분이다. 그렇다고 이런 정책이 결정될 때 까지 프로그래머는 개발을 무기한 연장할 수 도 없다.
따라서 객체 지향 설계 방법을 이용하여 프로그래밍하고, 인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계 해보겠다.
2. 회원 도메인 설계
회원 도메인을 잘 설계하면 기획자들도 볼 수 있는 그림이 될 수 있다. 요구사항 분석 과정에서 이해관계자들의 소통의 도구로도 사용된다.
- 회원 도메인 요구사항
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다.(미확정)
회원 DB가 미확정이므로 회원에 접근하는 계층 "회원 저장소"를 따로 만들어 주어야 한다.
회원 도메인 다이어그램을 바탕으로 더 구체화 하여 회원 클래스 다이어그램을 만들어주었다. 서버를 실제로 실행하지 않고도 클래스의 의존 관계만 보고 그릴 수 있는 다이어그램이다.
그러나, 서버가 뜰 때 동적으로 결정되는 부분은 클래스 다이어그램만으로 참고하기가 어렵다.
클래스다이어그램만 봐서는 MemoryMemberRepository를 사용하는지 DbMemberRepository를 사용하는지 알 수 없다.
** 회원 서비스 : MemberServiceImpl **
객체 다이어그램은 동적으로 맺어진 객체들의 연관관계를 표현한다.
따라서, 실제 서버가 띄워졌을 시 생성한 인스턴스끼리의 참조를 표현합니다.
3. 회원 도메인 개발
3-1.) 회원 Entity
먼저, src/java/test/blog
아래에 member라는 패키지를 만들어준다.
폴더명이 각자 다를 수 있으니 아래 사진 위치에 만들어 주면 된다.
member 패키지까지 만들었으면, 회원 등급에는 일반과 VIP 등급이 있으므로 회원 등급을 설정해주기 위해서 enum타입으로 member패키지 아래에 Grade.enum
파일을 생성, 회원 정보가 들어갈 Member.class
엔티티를 생성해준다.
**<Grade.enum>**
package test.blog.member;
public enum Grade {
BASIC, VIP
}
**<Member.class>**
package test.blog.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;
}
}
3-2.) 회원 저장소
그리고 회원 정보를 저장할 MemberRepository.interface
와MemoryMemberRepository.class
파일을 생성해준다.
**<MemberRepository.interface>**
package test.blog.member;
public interface MemberRepository {
void save(Member member);
Member findbyId(Long MemberId);
}
**<MemoryMemberRepository.class>**
package test.blog.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가 아직 확정이 안되었지만 개발을 진행함에 있어 가장 단순한 메모리 회원 저장소를 구현하였다.
Memer에 회원이 입력되면, MemberRepository 인터페이스에서 save함수를 통하여 MemoryMemberRepository에 회원을 저장하고, findbyId 함수를 통하여 회원 정보를 찾는 구조로 되어있다.
따라서 실제로 회원을 저장해주는 역할을 하는 것은 MemoryRepository가 아닌 MemoryMemberRepository다.
3-3.) 회원 서비스
회원 서비스 인터페이스와 회원 서비스 구현체를 만들어준다.
**<MemberService.interface>**
package test.blog.member;
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
**<MemberServiceImpl.class>**
package test.blog.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);
}
}
저장소에 성공적으로 회원 정보가 저장되었으면 join함수를 통해 저장된 멤버를 불러오고, MemberServiceImpl의 findMember을 통해 회원을 조회할 수 있다.
실제로 회원을 찾는 역할을 하는 것은 MemberService가 아닌 MemberServiceImpl이다.
4. 회원 도메인 실행과 테스트
scr/main/java/test/blog
아래에 MemberApp.class
파일을 생성해준다.
member 패키지가 아닌 blog패키지에 생성을 하여야 한다.
**<MemberApp.class>**
package test.blog;
import test.blog.member.Grade;
import test.blog.member.Member;
import test.blog.member.MemberService;
import test.blog.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);
test.blog.member.Member findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find member = " + findMember.getName());
}
}
파일을 생성 후, MemberApp.class
파일을 실행시켜준다.
위와 같이 결과창이 뜨면 성공적으로 실행이 되었다는 뜻이다.
그러나, 이런 애플리케이션 로직으로 테스트를 실행시키는 것은 좋은 방법은 아니므로
JUnit을 통하여 테스트를 해보겠다.
위와 같은 경로로 MemberServiceTest.class
파일을 생성해준다.
**<MemberServiceTest.class>**
package test.blog.member;
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);
}
}
결과창에 아무런 오류 없이 실행이 되었다면 성공적인 테스트 결과이다.
테스트 파일을 잘 만들어주고 항상 테스트를 실행시켜 아무런 오류가 검출되지 않는지 테스트해봐야 잘 짜여진 프로그래밍이라고 볼 수 있다.
5. 회원 도메인 설계의 문제점
그러나 이 회원 도메인 설계에는 문제점이 있다.OCP와 DIP를 잘 지키고 있지 않다는 점이다.
의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있다.
참고
Author And Source
이 문제에 관하여(Spring Basic #. 예제 비즈니스 요구사항과 설계), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gkstjdwns2/Spring-Basic-.-예제-비즈니스-요구사항과-설계저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)