Skip to content

Commit 5dcd2cc

Browse files
authored
🚀 3단계 - 수강신청(DB 적용) (#798)
* feat : 피드백 내용 반영 후 수정 진행 * docs : 나의 학습 목표, 요구사항 정리 후 파일 생성 * docs : 객체에 맞춰 sql 파일 작성 * refactor : session Repository 제작 후 연결 중...(또 열심히 코드 작성하다 보니 작업 단위별로 커밋하는 것을 잊어버렸습니다..) * feat : CoverImage Repository Sql 수정 및 테스트 코드 작성 * feat : sessionService findById 메서드 생성(Enrollments 작업 전) * feat : Enrollment Repository 생성 및 내부 로직 생성, 테스트 코드 작성, EnrollmentRecord 생성 refactor : sessionService 테스트 코드 수정, Enrollment에서 필요한 인스턴스 변수 추가(id) docs : sql Base Data 추가 * feat : SessionRepository에서 entrollment 추가 메서드 생성 및 테스트 코드 추가 docs : Base data 추가 * docs : Base data autoincrement 추가 refactor : repository insert 시 id 제거 * refactor : 생산성이 떨어지는 테스트 제거, 수강신청 시 Service에서 유효성 검사 진행, 강의 등록 시 Session 객체가 아닌 SessionRecord 객체 전달 * refactor : enrollment 객체에서 payment 인스턴스 변수 제거, 강의 신청 시 payment를 파라미터로 전달 받아 다시 session에게 전달 후 금액 일치 확인, 점진적인으로 리팩터링 진행 * feat : payment 테이블 생성, payment 저장 로직 생성
1 parent ac588bb commit 5dcd2cc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+877
-45
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 단계별 문서
44
- [🚀 1단계 - 레거시 코드 리팩터링](./docs/01-refactoring.md)
55
- [🚀 2단계 - 수강신청(도메인 모델)](./docs/02-lms-domain-model.md)
6+
- [🚀 3단계 - 수강신청(DB 적용)](./docs/03-lms-db.md)
67

78
## 진행 방법
89
* 학습 관리 시스템의 수강신청 요구사항을 파악한다.

docs/02-lms-domain-model.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
***
33

44
## 코드 리뷰
5-
> PR 링크 :
5+
> PR 링크 : [#793](https://github.com/next-step/java-lms/pull/793)
66
77
## 나의 학습 목표
88

99
### 1. 객체 중심의 설계를 해보자.
1010
- 요구사항을 정확히 분석하고, TDD 사이클을 통해 객체의 핵심 비즈니스 로직을 우선적으로 만들어보자.
1111
- 그 이후 객체를 조립하며, 리팩터링 해보자.
1212

13-
### 1. TDD 사이클을 의식하며 기능을 구현한다.
13+
### 2. TDD 사이클을 의식하며 기능을 구현한다.
1414
- `실패 → 성공 → 리팩터링` 과정으로 작업한다.
1515
- 습관처럼 이 과정을 지나치면, 다시 돌아와 사이클을 반복한다.
1616
- TDD 사이클 습관을 만들자.

docs/03-lms-db.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 🚀 3단계 - 수강신청(DB 적용)
2+
***
3+
4+
## 코드 리뷰
5+
> PR 링크 : [#798](https://github.com/next-step/java-lms/pull/798)
6+
7+
## 나의 학습 목표
8+
9+
### 1. 객체 주도 설계를 진행해보자
10+
- 평소 DB 설계 시 테이블 주도 설계로 진행해 왔지만 이번 기회에 교육 취지에 맞게 `객체 주도 설계`를 진행해보자.
11+
12+
### 2. 도메인 객체 수정을 최소화하자
13+
- 이전 단계에서 도메인 객체 설계를 진행했고, 이번 단계에서는 도메인 객체 수정을 최소화하며 DB 테이블과 매핑해보자.
14+
15+
16+
## 핵심 학습 목표
17+
18+
- 2단계에서 구현한 객체 구조(도메인 구조)를 가능한 유지하면서 DB 테이블과 매핑한다.
19+
- 성능보다 도메인 객체에 로직 구현하는 것을 목표로 연습한다
20+
- 객체 구조를 유지하기 위해 여러 번의 DB 쿼리를 실행해도 괜찮다.
21+
22+
## 프로그래밍 요구사항
23+
24+
- 앞 단계에서 구현한 도메인 모델을 DB 테이블과 매핑하고, 데이터를 저장한다.
25+
- CRUD 쿼리와 코드를 구현하는데 집중하기 보다 테이블을 설계하고 객체 매핑하는 부분에 집중한다.
26+
- Payment는 테이블 매핑을 고려하지 않아도 된다.
27+
28+
## 참고할 코드
29+
30+
- DB 테이블 추가 : src/main/resources/schema.sql
31+
32+
```sql
33+
create table course (
34+
id bigint generated by default as identity,
35+
title varchar(255) not null,
36+
creator_id bigint not null,
37+
created_at timestamp not null,
38+
updated_at timestamp,
39+
primary key (id)
40+
);
41+
```
42+
43+
- 테이블에 샘플 데이터를 추가하고 싶다면 src/main/resources/data.sql 파일에 추가 가능함
44+
45+
### CRUD 코드
46+
47+
- src/main/java 폴더의 nextstep.courses.infrastructure.JdbcCourseRepository
48+
- JdbcCourseRepository 샘플 코드는 Spring JDBC 라이브러리를 활용해 구현함
49+
50+
### CRUD 코드에 대한 테스트 코드
51+
52+
- src/test/java 폴더의 nextstep.courses.infrastructure.CourseRepositoryTest

docs/check-list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- TDD 사이클로 구현하였는가 ?
66
- TDD 사이클 단위로 커밋하였는가 ?
77
- 객체에게 상태를 노출시키지 않고 책임을 맡겼는가 ?
8-
- 테슽으는 객체의 책임과 결과만 검증했는가 ?
8+
- 테스트는 객체의 책임과 결과만 검증했는가 ?
99
- 불변성을 최대한 유지했는가 ?
1010
- 도메인 용어로 네이밍 했는가 ?
1111
- 협력 구조가 자연스럽게 읽히는가 ?

src/main/java/nextstep/courses/domain/Course.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public void addSession(Session session) {
3838
this.sessions.add(session);
3939
}
4040

41+
public Long getId() {
42+
return id;
43+
}
44+
4145
public String getTitle() {
4246
return title;
4347
}

src/main/java/nextstep/courses/domain/image/CoverImage.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class CoverImage {
66

77
public static final double MAX_FILE_SIZE = 1024 * 1024; // 1MB
88

9+
private Long id;
910
private final long size;
1011
private final ImageType type;
1112
private final ImageDimentsion dimentsion;
@@ -14,12 +15,21 @@ public CoverImage(long size, String type, int width, int height) {
1415
this(size, ImageType.fromName(type.toUpperCase()), width, height);
1516
}
1617

18+
public CoverImage(Long id, long size, String type, int width, int height) {
19+
this(id, size, ImageType.fromName(type.toUpperCase()), width, height);
20+
}
21+
1722
public CoverImage(long size, ImageType type, int width, int height) {
18-
this(size, type, new ImageDimentsion(width, height));
23+
this(0L, size, type, new ImageDimentsion(width, height));
1924
}
2025

21-
public CoverImage(long size, ImageType type, ImageDimentsion dimentsion) {
26+
public CoverImage(Long id, long size, ImageType type, int width, int height) {
27+
this(id, size, type, new ImageDimentsion(width, height));
28+
}
29+
30+
public CoverImage(Long id, long size, ImageType type, ImageDimentsion dimentsion) {
2231
validateFileSize(size);
32+
this.id = id;
2333
this.size = size;
2434
this.type = type;
2535
this.dimentsion = dimentsion;
@@ -31,6 +41,10 @@ private void validateFileSize(long size) {
3141
}
3242
}
3343

44+
public Long getId() {
45+
return id;
46+
}
47+
3448
public long getSize() {
3549
return size;
3650
}
@@ -39,5 +53,12 @@ public ImageType getType() {
3953
return type;
4054
}
4155

56+
public int getWidth(){
57+
return this.dimentsion.getWidth();
58+
}
59+
60+
public int getHeight(){
61+
return this.dimentsion.getHeight();
62+
}
4263

4364
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package nextstep.courses.domain.image;
2+
3+
public interface CoverImageRepository {
4+
5+
int save(CoverImage coverImage);
6+
7+
CoverImage findById(Long id);
8+
}

src/main/java/nextstep/courses/domain/image/ImageDimentsion.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,12 @@ private void validateSize(int width, int height) {
3232
private static boolean isSize(int width, int height) {
3333
return width < MIN_WIDTH || height < MIN_HEIGHT;
3434
}
35+
36+
public int getWidth() {
37+
return width;
38+
}
39+
40+
public int getHeight() {
41+
return height;
42+
}
3543
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package nextstep.courses.domain.service;
2+
3+
import nextstep.courses.domain.Course;
4+
import nextstep.courses.domain.CourseRepository;
5+
import nextstep.courses.domain.image.CoverImage;
6+
import nextstep.courses.domain.image.CoverImageRepository;
7+
import nextstep.courses.domain.session.Enrollment;
8+
import nextstep.courses.domain.session.Enrollments;
9+
import nextstep.courses.domain.session.Session;
10+
import nextstep.courses.domain.session.repository.EnrollmentRepository;
11+
import nextstep.courses.domain.session.repository.SessionRepository;
12+
import nextstep.courses.record.EnrollmentRecord;
13+
import nextstep.courses.record.SessionRecord;
14+
import nextstep.payments.domain.Payment;
15+
import nextstep.payments.repository.PaymentRepository;
16+
import nextstep.users.domain.NsUser;
17+
import nextstep.users.domain.UserRepository;
18+
import org.springframework.stereotype.Service;
19+
import org.springframework.transaction.annotation.Transactional;
20+
21+
import java.util.List;
22+
import java.util.Optional;
23+
24+
@Service
25+
public class SessionService {
26+
27+
private final SessionRepository sessionRepository;
28+
private final CoverImageRepository coverImageRepository;
29+
private final CourseRepository courseRepository;
30+
private final EnrollmentRepository enrollmentRepository;
31+
private final UserRepository userRepository;
32+
private final PaymentRepository paymentRepository;
33+
34+
public SessionService(SessionRepository sessionRepository,
35+
CoverImageRepository coverImageRepository,
36+
CourseRepository courseRepository,
37+
EnrollmentRepository enrollmentRepository,
38+
UserRepository userRepository,
39+
PaymentRepository paymentRepository) {
40+
this.sessionRepository = sessionRepository;
41+
this.coverImageRepository = coverImageRepository;
42+
this.courseRepository = courseRepository;
43+
this.enrollmentRepository = enrollmentRepository;
44+
this.userRepository = userRepository;
45+
this.paymentRepository = paymentRepository;
46+
}
47+
48+
public Session findById(Long id) {
49+
SessionRecord sessionRecord = sessionRepository.findById(id);
50+
CoverImage saveCoverImage = coverImageRepository.findById(sessionRecord.getCoverImageId());
51+
Course saveCourse = courseRepository.findById(sessionRecord.getCourseId());
52+
List<EnrollmentRecord> enrollmentRecords = enrollmentRepository.findBySessionId(id);
53+
Enrollments enrollments = toEnrollments(enrollmentRecords);
54+
55+
return sessionRecord.toSession(saveCourse, saveCoverImage, enrollments);
56+
}
57+
58+
@Transactional
59+
public int save(Session session) {
60+
coverImageRepository.save(session.getCoverImage());
61+
return sessionRepository.save(session.toSessionRecord());
62+
}
63+
64+
@Transactional
65+
public int saveEnrollment(Enrollment enrollment, Payment payment) {
66+
SessionRecord sessionRecord = sessionRepository.findById(enrollment.getSessionId());
67+
List<EnrollmentRecord> enrollmentRecords = enrollmentRepository.findBySessionId(enrollment.getSessionId());
68+
Session session = sessionRecord.toSession(null, null, toEnrollments(enrollmentRecords));
69+
session.addEnrollment(enrollment, payment);
70+
71+
paymentRepository.save(payment);
72+
73+
return enrollmentRepository.save(enrollment);
74+
}
75+
76+
private Enrollments toEnrollments(List<EnrollmentRecord> enrollmentRecords) {
77+
Enrollments enrollments = new Enrollments();
78+
79+
for (EnrollmentRecord enrollmentRecord : enrollmentRecords) {
80+
Optional<NsUser> user = userRepository.findById(enrollmentRecord.getUserId());
81+
enrollments.add(enrollmentRecord.toEnrollment(user.orElse(null)));
82+
}
83+
84+
return enrollments;
85+
}
86+
87+
88+
}
Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
11
package nextstep.courses.domain.session;
22

3-
import nextstep.payments.domain.Payment;
43
import nextstep.users.domain.NsUser;
54

65
import java.util.Objects;
76

87
public class Enrollment {
98

9+
private final Long id;
1010
private final NsUser user;
1111
private final Long sessionId;
12-
private final Payment payment;
1312

14-
public Enrollment(NsUser user, Long sessionId, Payment payment) {
13+
public Enrollment(NsUser user, Long sessionId) {
14+
this(0L, user, sessionId);
15+
}
16+
17+
public Enrollment(Long id, NsUser user, Long sessionId) {
18+
this.id = id;
1519
this.user = user;
1620
this.sessionId = sessionId;
17-
this.payment = payment;
1821
}
1922

20-
public void isPaymentAmount(SessionPolicy sessionPolicy) {
21-
if(sessionPolicy.matchAmount(payment)){
22-
throw new IllegalArgumentException("강의 금액과 결제 금액이 일치하지 않습니다.");
23-
}
23+
public Long getId() {
24+
return id;
25+
}
26+
27+
public NsUser getUser() {
28+
return user;
29+
}
30+
31+
public Long getSessionId() {
32+
return sessionId;
2433
}
2534

2635
@Override
@@ -32,7 +41,15 @@ public boolean equals(Object o) {
3241

3342
@Override
3443
public int hashCode() {
35-
return Objects.hash(user, sessionId, payment);
44+
return Objects.hash(user, sessionId);
3645
}
3746

47+
@Override
48+
public String toString() {
49+
return "Enrollment{" +
50+
"id=" + id +
51+
", user=" + user +
52+
", sessionId=" + sessionId +
53+
'}';
54+
}
3855
}

0 commit comments

Comments
 (0)