Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸš€ 4단계 - μˆ˜κ°•μ‹ μ²­(μš”κ΅¬μ‚¬ν•­ λ³€κ²½) #387

Open
wants to merge 8 commits into
base: kyunghyun-park
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions md/step4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# πŸš€ 4단계 - μˆ˜κ°•μ‹ μ²­(μš”κ΅¬μ‚¬ν•­ λ³€κ²½)

## 핡심 ν•™μŠ΅ λͺ©ν‘œ

- DB ν…Œμ΄λΈ”μ΄ 변경될 λ•Œλ„ μŠ€νŠΈλž­κΈ€λŸ¬ νŒ¨ν„΄μ„ μ μš©ν•΄ 점진적인 λ¦¬νŒ©ν„°λ§μ„ μ—°μŠ΅ν•œλ‹€.
- [μŠ€νŠΈλž­κΈ€λŸ¬(κ΅μ‚΄μž) νŒ¨ν„΄ - λ§ˆν‹΄ 파울러](https://martinfowler.com/bliki/StranglerFigApplication.html)
- [μŠ€νŠΈλž­κΈ€λŸ¬ 무화과 νŒ¨ν„΄](https://docs.microsoft.com/ko-kr/azure/architecture/patterns/strangler-fig)

<br>

## λ³€κ²½λœ κΈ°λŠ₯ μš”κ΅¬μ‚¬ν•­
### **κ°•μ˜ μˆ˜κ°•μ‹ μ²­μ€ κ°•μ˜ μƒνƒœκ°€ λͺ¨μ§‘쀑일 λ•Œλ§Œ κ°€λŠ₯ν•˜λ‹€.**
- [X] κ°•μ˜κ°€ 진행 쀑인 μƒνƒœμ—μ„œλ„ μˆ˜κ°•μ‹ μ²­μ΄ κ°€λŠ₯ν•΄μ•Ό ν•œλ‹€.
- κ°•μ˜ 진행 μƒνƒœ(쀀비쀑, 진행쀑, μ’…λ£Œ)와 λͺ¨μ§‘ μƒνƒœ(λΉ„λͺ¨μ§‘쀑, λͺ¨μ§‘쀑)둜 μƒνƒœ 값을 뢄리해야 ν•œλ‹€.

### **κ°•μ˜λŠ” κ°•μ˜ 컀버 이미지 정보λ₯Ό 가진닀.**
- [X] κ°•μ˜λŠ” **ν•˜λ‚˜ 이상**의 컀버 이미지λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€.

### 강사가 μŠΉμΈν•˜μ§€ μ•Šμ•„λ„ μˆ˜κ°• μ‹ μ²­ν•˜λŠ” λͺ¨λ“  μ‚¬λžŒμ΄ μˆ˜κ°• κ°€λŠ₯ν•˜λ‹€.
- [ ] μš°μ•„ν•œν…Œν¬μ½”μŠ€(무료), μš°μ•„ν•œν…Œν¬μΊ ν”„ Pro(유료)와 같이 μ„ λ°œλœ μΈμ›λ§Œ μˆ˜κ°• κ°€λŠ₯ν•΄μ•Ό ν•œλ‹€.
- [ ] κ°•μ‚¬λŠ” μˆ˜κ°•μ‹ μ²­ν•œ μ‚¬λžŒ 쀑 μ„ λ°œλœ 인원에 λŒ€ν•΄μ„œλ§Œ μˆ˜κ°• 승인이 κ°€λŠ₯ν•΄μ•Ό ν•œλ‹€.
- [ ] κ°•μ‚¬λŠ” μˆ˜κ°•μ‹ μ²­ν•œ μ‚¬λžŒ 쀑 μ„ λ°œλ˜μ§€ μ•Šμ€ μ‚¬λžŒμ€ μˆ˜κ°•μ„ μ·¨μ†Œν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.

<br>

## ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬μ‚¬ν•­
- λ¦¬νŒ©ν„°λ§ν•  λ•Œ 컴파일 μ—λŸ¬μ™€ 기쑴의 λ‹¨μœ„ ν…ŒμŠ€νŠΈμ˜ μ‹€νŒ¨λ₯Ό μ΅œμ†Œν™”ν•˜λ©΄μ„œ 점진적인 λ¦¬νŒ©ν„°λ§μ΄ κ°€λŠ₯ν•˜λ„λ‘ ν•œλ‹€.
- DB ν…Œμ΄λΈ”μ— 데이터가 μ‘΄μž¬ν•œλ‹€λŠ” κ°€μ •ν•˜μ— λ¦¬νŒ©ν„°λ§ν•΄μ•Ό ν•œλ‹€.
- 즉, 기쑴에 μŒ“μΈ 데이터λ₯Ό μ œκ±°ν•˜μ§€ μ•Šμ€ μƒνƒœλ‘œ λ¦¬νŒ©ν„°λ§ ν•΄μ•Ό ν•œλ‹€.
21 changes: 21 additions & 0 deletions src/main/java/nextstep/courses/domain/CoverImages.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package nextstep.courses.domain;

import nextstep.courses.exception.EmptyCoverImageException;

import java.util.List;

public class CoverImages {

private List<CoverImage> images;

public CoverImages(List<CoverImage> images) {
validateIsNull(images);
this.images = images;
}

private void validateIsNull(List<CoverImage> images) {
if (images == null || images.isEmpty()) {
throw new EmptyCoverImageException();
}
}
}
4 changes: 0 additions & 4 deletions src/main/java/nextstep/courses/domain/Period.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ private void validatePeriod(LocalDate startDate, LocalDate endDate) {
}
}

public boolean isDateWithinRange(LocalDate date) {
return !date.isBefore(startDate) && !date.isAfter(endDate);
}

public LocalDate startDate() {
return startDate;
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/nextstep/courses/domain/RecruitmentStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nextstep.courses.domain;

import nextstep.courses.exception.InvalidRecruitmentStatusException;

import java.util.Arrays;

public enum RecruitmentStatus {
NOT_RECRUITMENT,
RECRUITING;

private static final RecruitmentStatus[] VALUES = values();

public static RecruitmentStatus findByName(String name) {
return Arrays.stream(VALUES)
.filter(status -> status.name().equalsIgnoreCase(name))
.findFirst()
.orElseThrow(() -> new InvalidRecruitmentStatusException(name));
}

public RecruitmentStatus ofRecruiting() {
return RECRUITING;
}

public boolean isRecruiting() {
return this == RECRUITING;
}
}
58 changes: 24 additions & 34 deletions src/main/java/nextstep/courses/domain/Session.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package nextstep.courses.domain;

import nextstep.courses.exception.InvalidSessionException;
import nextstep.courses.exception.NotOpenSessionException;
import nextstep.courses.exception.OutOfSessionException;
import nextstep.payments.domain.Payment;

import java.time.LocalDate;
Expand All @@ -13,39 +11,39 @@ public class Session extends BaseEntity {
private final Long id;
private final Long courseId;
private final SessionType type;
private final CoverImage coverImage;
private final CoverImages coverImages;
private final Period period;
private Status status;
private final Status status;
private final Students students;
private final PaidCondition paidCondition;

public static Session ofFree(Long id, Long courseId, CoverImage coverImage, LocalDate startDate, LocalDate endDate) {
return new Session(id, courseId, SessionType.FREE, coverImage, new Period(startDate, endDate), Status.NOT_OPEN, 0, 0L, LocalDateTime.now(), null);
public static Session ofFree(Long id, Long courseId, CoverImages coverImages, LocalDate startDate, LocalDate endDate) {
return new Session(id, courseId, SessionType.FREE, coverImages, new Period(startDate, endDate), new Status(LocalDate.now(), startDate, endDate), 0, 0L, LocalDateTime.now(), null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Statusκ°€ μ‘°νšŒν•΄μ˜¨ λ°μ΄ν„°μ˜ λ‚ μ§œλ₯Ό κΈ°μ€€μœΌλ‘œ μ„€μ •λ˜λŠ”λ°, μž„μ˜λ‘œ μƒνƒœκ°€ λ³€κ²½λ˜λ©΄ κ°•μ‚¬μ—κ²Œ ν˜Όλž€μ΄ λ°œμƒν•  수 μžˆμ„ κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€. Status의 변경은 λͺ…μ‹œμ μœΌλ‘œ 강사가 μ§„ν–‰ν•œλ‹€λŠ” μ „μ œν•˜μ— κ΅¬ν˜„ν•΄λ³΄μ‹œλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€.

ν˜„μ—…μ—μ„œλŠ” λ‚ μ§œκ°€ 경과되면 배치 등을 μ΄μš©ν•΄μ„œ μžλ™μœΌλ‘œ Statusκ°€ λ³€κ²½λ˜λ„λ‘ κ΅¬ν˜„ν•˜μ‹€ 수 μžˆμ„ν…λ°, 이 경우 데이터λ₯Ό μˆ˜μ •ν•˜μ‹€ 것 κ°™μ•„μš”. 객체λ₯Ό λ§Œλ“€ λ•Œ μž„μ˜λ‘œ μƒνƒœλ₯Ό λ§Œλ“œλŠ” 것은 μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κ±°λ‚˜ μœ μ§€λ³΄μˆ˜ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€ 수 있으며, 디버깅 κ³Όμ •μ—μ„œλ„ 어렀움이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

}

public static Session ofPaid(Long id, Long courseId, CoverImage coverImage, LocalDate startDate, LocalDate endDate, int maxStudents, Long fee) {
return new Session(id, courseId, SessionType.PAID, coverImage, new Period(startDate, endDate), Status.NOT_OPEN, maxStudents, fee, LocalDateTime.now(), null);
public static Session ofPaid(Long id, Long courseId, CoverImages coverImages, LocalDate startDate, LocalDate endDate, int maxStudents, Long fee) {
return new Session(id, courseId, SessionType.PAID, coverImages, new Period(startDate, endDate), new Status(LocalDate.now(), startDate, endDate), maxStudents, fee, LocalDateTime.now(), null);
}

public static Session of(Long id, Long courseId, SessionType type, CoverImage coverImage, Status status, LocalDate startDate, LocalDate endDate, int maxStudents, Long fee, LocalDateTime createdAt, LocalDateTime updatedAt) {
return new Session(id, courseId, type, coverImage, new Period(startDate, endDate), status, maxStudents, fee, createdAt, updatedAt);
public static Session of(Long id, Long courseId, SessionType type, CoverImages coverImages, RecruitmentStatus recruitmentStatus, LocalDate startDate, LocalDate endDate, int maxStudents, Long fee, LocalDateTime createdAt, LocalDateTime updatedAt) {
return new Session(id, courseId, type, coverImages, new Period(startDate, endDate), new Status(LocalDate.now(), startDate, endDate, recruitmentStatus), maxStudents, fee, createdAt, updatedAt);
}

private Session(Long id, Long courseId, SessionType type, CoverImage coverImage, Period period, Status status, int maxStudents, Long fee, LocalDateTime createdAt, LocalDateTime updatedAt) {
private Session(Long id, Long courseId, SessionType type, CoverImages coverImages, Period period, Status status, int maxStudents, Long fee, LocalDateTime createdAt, LocalDateTime updatedAt) {
super(createdAt, updatedAt);
validateNotNull(id, coverImage, period);
validateNotNull(id, coverImages, period);
this.id = id;
this.courseId = courseId;
this.type = type;
this.coverImage = coverImage;
this.coverImages = coverImages;
this.period = period;
this.status = status;
this.students = new Students();
this.paidCondition = new PaidCondition(maxStudents, fee);
}

private void validateNotNull(Long id, CoverImage coverImage, Period period) {
if (id == null || coverImage == null || period == null) {
private void validateNotNull(Long id, CoverImages coverImages, Period period) {
if (id == null || coverImages == null || period == null) {
throw new InvalidSessionException();
}
}
Expand All @@ -59,20 +57,11 @@ public void register(Payment payment) {
}

protected void validateStatus() {
if (!status.isOpen()) {
throw new NotOpenSessionException();
}
}

public void openSession() {
if (!period.isDateWithinRange(LocalDate.now())) {
throw new OutOfSessionException();
}
changeStatusOpen();
this.status.validate();
}

private void changeStatusOpen() {
this.status = status.ofOpen();
public void startRecruiting() {
this.status.startRecruiting();
}

public Long id() {
Expand All @@ -83,16 +72,16 @@ public Long courseId() {
return courseId;
}

public Long imageId() {
return coverImage.getId();
}

public String type() {
return type.name();
}

public String status() {
return status.name();
public String sessionStatus() {
return status.sessionStatus();
}

public String recruitmentStatus() {
return status.recruitmentStatus();
}

public LocalDate startDate() {
Expand All @@ -115,8 +104,9 @@ public Long fee() {
public String toString() {
return "Session{" +
"id=" + id +
", courseId=" + courseId +
", type=" + type +
", coverImage=" + coverImage +
", coverImages=" + coverImages +
", period=" + period +
", status=" + status +
", students=" + students +
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/nextstep/courses/domain/SessionStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package nextstep.courses.domain;

import java.time.LocalDate;

public enum SessionStatus {
PREPARING,
IN_PROGRESS,
COMPLETED;

public static SessionStatus of(LocalDate now, LocalDate startDate, LocalDate endDate) {
if (now.isBefore(startDate)) {
return PREPARING;
}

if (now.isAfter(startDate) && now.isBefore(endDate)) {
return IN_PROGRESS;
}

return COMPLETED;
}

public boolean isInProgress() {
return this == IN_PROGRESS;
}
}
45 changes: 30 additions & 15 deletions src/main/java/nextstep/courses/domain/Status.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
package nextstep.courses.domain;

import nextstep.courses.exception.InvalidSessionStatusException;
import nextstep.courses.exception.NotOpenSessionException;

import java.util.Arrays;
import java.time.LocalDate;

public enum Status {
NOT_OPEN,
OPEN,
CLOSED;
public class Status {

private SessionStatus sessionStatus;
private RecruitmentStatus recruitmentStatus;

public Status ofOpen() {
return OPEN;
public Status(LocalDate now, LocalDate startDate, LocalDate endDate) {
this(SessionStatus.of(now, startDate, endDate), RecruitmentStatus.NOT_RECRUITMENT);
}

public boolean isOpen() {
return this == OPEN;
public Status(LocalDate now, LocalDate startDate, LocalDate endDate, RecruitmentStatus recruitmentStatus) {
this(SessionStatus.of(now, startDate, endDate), recruitmentStatus);
}

public static Status findByName(String name) {
return Arrays.stream(values())
.filter(status -> status.name().equalsIgnoreCase(name))
.findFirst()
.orElseThrow(() -> new InvalidSessionStatusException(name));
public Status(SessionStatus sessionStatus, RecruitmentStatus recruitmentStatus) {
this.sessionStatus = sessionStatus;
this.recruitmentStatus = recruitmentStatus;
}

public void validate() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status μž…μž₯μ—μ„œ 이 λ©”μ„œλ“œκ°€ μ œκ³΅ν•΄μ•Ό ν•˜λŠ” κΈ°λŠ₯은 μˆ˜κ°• 신청이 κ°€λŠ₯ν•œμ§€ μ—¬λΆ€λ₯Ό λ°˜ν™˜ν•˜λŠ” κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.
StatusλŠ” μˆ˜κ°• 신청이 κ°€λŠ₯ν•œ μƒνƒœμΈμ§€ μ—¬λΆ€λ§Œ λ°˜ν™˜ν•˜κ³ , Session은 이λ₯Ό μ°Έκ³ ν•˜μ—¬ μˆ˜κ°• 신청이 λΆˆκ°€ν•  경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚€λ„λ‘ 해보면 μ–΄λ–¨κΉŒμš”?

if (!sessionStatus.isInProgress() && !recruitmentStatus.isRecruiting()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sessionStatus만 μ‘΄μž¬ν–ˆλ˜ μ„Έμ…˜μ΄λΌλ©΄(recruitmentStatusκ°€ μ—†κ±°λ‚˜, sessionStatusκ°€ λͺ¨μ§‘쀑인 경우) sessionStatusλ§Œμ„ μ΄μš©ν•˜μ—¬ μˆ˜κ°• 신청이 κ°€λŠ₯ν•œμ§€ 확인할 수 μžˆμ„ κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€. recruitmentStatusκ°€ μ‘΄μž¬ν•œλ‹€λ©΄, 이 값을 κΈ°μ€€μœΌλ‘œ μˆ˜κ°• 신청이 κ°€λŠ₯ν•œμ§€ 확인할 수 μžˆμ„ κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.
μ°Έκ³ ν•˜μ‹œμ–΄ κΈ°μ‘΄ 데이터(sessionStatus만 μžˆλŠ” 경우)와 μƒˆλ‘œμš΄ 데이터(recruitmentStatusκ°€ μžˆλŠ” 경우) λͺ¨λ‘ λ™μž‘ν•˜λ„λ‘, 이 λ‘œμ§μ„ μˆ˜μ •ν•΄λ³΄μ‹œλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€. :)

throw new NotOpenSessionException();
}
}

public void startRecruiting() {
this.recruitmentStatus = recruitmentStatus.ofRecruiting();
}

public String sessionStatus() {
return this.sessionStatus.name();
}

public String recruitmentStatus() {
return this.recruitmentStatus.name();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.courses.exception;

public class EmptyCoverImageException extends RuntimeException {

public EmptyCoverImageException() {
super("κ°•μ˜λŠ” ν•˜λ‚˜ μ΄μƒμ˜ 컀버 이미지λ₯Ό κ°€μ Έμ•Ό ν•©λ‹ˆλ‹€.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.courses.exception;

public class InvalidRecruitmentStatusException extends RuntimeException {

public InvalidRecruitmentStatusException(String code) {
super("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ°•μ˜ λͺ¨μ§‘ μƒνƒœμž…λ‹ˆλ‹€." + code);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class NotOpenSessionException extends RuntimeException {
public NotOpenSessionException() {
super("λͺ¨μ§‘ 쀑인 κ°•μ˜κ°€ μ•„λ‹™λ‹ˆλ‹€.");
super("진행 μ€‘μ΄κ±°λ‚˜ λͺ¨μ§‘ 쀑인 κ°•μ˜κ°€ μ•„λ‹™λ‹ˆλ‹€.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;

@Repository("coverImageRepository")
public class JdbcCoverImageRepository implements CoverImageRepository {
Expand All @@ -19,23 +20,33 @@ public JdbcCoverImageRepository(JdbcOperations jdbcTemplate) {
}

@Override
public int save(CoverImage image) {
String sql = "insert into cover_image (id, size, extension, width, height, created_at) values(?, ?, ?, ?, ?, ?)";
return jdbcTemplate.update(sql, image.getId(), image.getSize(), image.getExtension(), image.getWidth(), image.getHeight(), image.getCreatedAt());
public int save(CoverImage image, Long sessionId) {
String sql = "insert into cover_image (id, session_id, size, extension, width, height, created_at) values(?, ?, ?, ?, ?, ?, ?)";
return jdbcTemplate.update(sql, image.getId(), sessionId, image.getSize(), image.getExtension(), image.getWidth(), image.getHeight(), image.getCreatedAt());
}

@Override
public CoverImage findById(Long id) {
String sql = "select id, size, extension, width, height, created_at, updated_at from cover_image where id = ?";
String sql = "select id, session_id, size, extension, width, height, created_at, updated_at from cover_image where id = ?";
return jdbcTemplate.queryForObject(sql, rowMapper(), id);
}

@Override
public List<CoverImage> findAllBySessionId(Long id) {
String sql = "select id, session_id, size, extension, width, height, created_at, updated_at from cover_image where session_id = ?";
return jdbcTemplate.query(sql, rowMapper(), id);
}

private RowMapper<CoverImage> rowMapper() {
RowMapper<CoverImage> rowMapper = (rs, rowNum) -> new CoverImage(
rs.getLong(1),
rs.getLong(2),
rs.getString(3),
rs.getInt(4),
rs.getLong(3),
rs.getString(4),
rs.getInt(5),
toLocalDateTime(rs.getTimestamp(6)),
toLocalDateTime(rs.getTimestamp(7)));
return jdbcTemplate.queryForObject(sql, rowMapper, id);
rs.getInt(6),
toLocalDateTime(rs.getTimestamp(7)),
toLocalDateTime(rs.getTimestamp(8)));
return rowMapper;
}

private LocalDateTime toLocalDateTime(Timestamp timestamp) {
Expand Down
Loading