-
Notifications
You must be signed in to change notification settings - Fork 308
2단계 - 수강신청(도메인 모델) #818
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
Merged
Merged
2단계 - 수강신청(도메인 모델) #818
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
d0abcf2
docs: 수강 신청 기능 목록 정리
username0w ec941aa
feat: startDate/endDate 검증 구현
username0w 5f35548
feat: 생성 시 기본 상태 PREPARING 설정
username0w b4b63b8
feat: 유료/무료 구분 및 최대 수강인원 검증 구현
username0w 61d5b30
feat: 유료 강의 fee 필드 및 검증 추가
username0w 0ed117d
feat: 상태 기반 수강 신청 가능 여부 구현
username0w 5a09362
feat: 유료 강의 최대 수강 인원 초과 시 수강 불가 검증 추가
username0w f67bd43
feat: 수강 신청 기능 구현
username0w 5901d8f
feat: SessionImage 생성 및 필수 검증 추가
username0w bce1fbd
test: SessionTestBuilder 추가 및 테스트에 적용
username0w 3d04dca
feat: Session에 SessionImage 필드 추가 및 생성자 검증
username0w 3d8f3a0
feat: 수강 기간, 수강 정원, 수강료 정보를 담는 VO 추가
username0w 1de7c10
refactor: Session에 Period, Capacity, SessionPricing 적용
username0w 3151975
feat: Session 관련 불변 정보 SessionInfo 추가
username0w 35191a7
feat: EnrollmentPolicy 인터페이스 및 구현체 추가
username0w 8340a2c
feat: SessionPricing을 Session으로 이동 and SessionInfo 필드 일부 외부 추출, Enrol…
username0w e834e6d
feat: ImageDimension 추가
username0w bbbc5b7
feat: ImageSize 추가
username0w 23c29f0
feat: ImageSize, ImageDimension 적용
username0w 61fd7b1
feat: FileName 추가
username0w cc4028c
feat: FileName 적용
username0w 3d841cf
feat: Enrollment 추가
username0w 9b6e440
feat: Enrollment, Enrollments 추가
username0w 7766878
feat: Enrollment, Enrollments 적용, Capacity unlimited 필드 추가
username0w 6e6c5fd
feat: 사용하지 않는 EnrollmentPolicy 제거
username0w ab7d29e
feat: 정적 팩토리 도입으로 불가능한 도메인 상태 생성 방지
username0w d4b5dd9
feat: Pricing과 Payment 비교 로직 이동
username0w 61df420
refactor: Stream 사용, 예외 메시지 상수화
username0w b39dd60
refactor: Capacity에서 수강 인원 상태 제거
username0w f20a4b7
refactor: 수강신청 로직을 SessionEnrollment 객체로 분리
username0w a53b64e
refactor: sessions 패키지 분리
username0w File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package nextstep.sessions.domain; | ||
|
|
||
| import java.time.LocalDate; | ||
|
|
||
| public class Period { | ||
|
|
||
| private static final String ERROR_INVALID_DATE = "시작일이 종료일보다 빨라야 합니다"; | ||
|
|
||
| private final LocalDate startDate; | ||
|
|
||
| private final LocalDate endDate; | ||
|
|
||
| public Period(LocalDate startDate, LocalDate endDate) { | ||
| validateDate(startDate, endDate); | ||
| this.startDate = startDate; | ||
| this.endDate = endDate; | ||
| } | ||
|
|
||
| private void validateDate(LocalDate startDate, LocalDate endDate) { | ||
| if (startDate.isAfter(endDate)) { | ||
| throw new IllegalArgumentException(ERROR_INVALID_DATE); | ||
| } | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package nextstep.sessions.domain; | ||
|
|
||
| import java.time.LocalDate; | ||
| import nextstep.sessions.domain.enrollment.Enrollment; | ||
| import nextstep.sessions.domain.enrollment.SessionEnrollment; | ||
| import nextstep.sessions.domain.image.SessionImage; | ||
|
|
||
| public class Session { | ||
|
|
||
| public static final String ERROR_SESSION_NOT_OPEN = "모집중인 강의만 수강 신청 가능합니다."; | ||
| public static final String ERROR_CAPACITY_EXCEEDED = "강의 정원이 초과되어 수강 신청할 수 없습니다."; | ||
| public static final String ERROR_PAYMENT_AMOUNT_MISMATCH = "결제 금액이 강의 수강료와 일치하지 않습니다."; | ||
|
|
||
| private Long id; | ||
|
|
||
| private SessionInfo sessionInfo; | ||
|
|
||
| private SessionEnrollment sessionEnrollment; | ||
|
|
||
| Session(Long id, SessionInfo sessionInfo, SessionEnrollment sessionEnrollment) { | ||
| this.id = id; | ||
| this.sessionInfo = sessionInfo; | ||
| this.sessionEnrollment = sessionEnrollment; | ||
| } | ||
|
|
||
| public static Session paidLimited(Long id, LocalDate startDate, LocalDate endDate, int fee, int maxCapacity, | ||
| SessionImage image) { | ||
| SessionInfo info = new SessionInfo(new Period(startDate, endDate), image); | ||
| SessionEnrollment enrollment = SessionEnrollment.paidLimited(fee, maxCapacity); | ||
| return new Session(id, info, enrollment); | ||
| } | ||
|
|
||
| public static Session freeUnlimited(Long id, LocalDate startDate, LocalDate endDate, SessionImage image) { | ||
| SessionInfo info = new SessionInfo(new Period(startDate, endDate), image); | ||
| SessionEnrollment enrollment = SessionEnrollment.freeUnlimited(); | ||
| return new Session(id, info, enrollment); | ||
| } | ||
|
|
||
| public SessionStatus status() { | ||
| return sessionEnrollment.status(); | ||
| } | ||
|
|
||
| public void startRecruiting() { | ||
| sessionEnrollment.startRecruiting(); | ||
| } | ||
|
|
||
| public void enroll(Enrollment enrollment) { | ||
| sessionEnrollment.enroll(enrollment); | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package nextstep.sessions.domain; | ||
|
|
||
| import nextstep.sessions.domain.image.SessionImage; | ||
|
|
||
| public class SessionInfo { | ||
|
|
||
| static final String ERROR_COVER_IMAGE_REQUIRED = "강의 커버 이미지는 필수입니다"; | ||
|
|
||
| private final Period period; | ||
|
|
||
| private SessionImage image; | ||
|
|
||
| public SessionInfo(Period period, SessionImage image) { | ||
| validateImage(image); | ||
| this.period = period; | ||
| this.image = image; | ||
| } | ||
|
|
||
| private static void validateImage(SessionImage image) { | ||
| if (image == null) { | ||
| throw new IllegalArgumentException(ERROR_COVER_IMAGE_REQUIRED); | ||
| } | ||
| } | ||
|
|
||
| } |
43 changes: 43 additions & 0 deletions
43
src/main/java/nextstep/sessions/domain/SessionPricing.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package nextstep.sessions.domain; | ||
|
|
||
| public class SessionPricing { | ||
|
|
||
| private static final String ERROR_PAID_FEE = "유료 강의는 0원 초과 여야 합니다"; | ||
| private static final String ERROR_FREE_FEE = "무료 강의는 0원 이어야 합니다"; | ||
|
|
||
| private final boolean isPaid; | ||
| private final int fee; | ||
|
|
||
| SessionPricing(boolean isPaid, int fee) { | ||
| validateFee(isPaid, fee); | ||
| this.isPaid = isPaid; | ||
| this.fee = fee; | ||
| } | ||
|
|
||
| public static SessionPricing paid(int fee) { | ||
| return new SessionPricing(true, fee); | ||
| } | ||
|
|
||
| public static SessionPricing free() { | ||
| return new SessionPricing(false, 0); | ||
| } | ||
|
|
||
| public boolean isPaid() { | ||
| return isPaid; | ||
| } | ||
|
|
||
| public int fee() { | ||
| return fee; | ||
| } | ||
|
|
||
| private void validateFee(boolean isPaid, int fee) { | ||
| if (isPaid && fee <= 0) { | ||
| throw new IllegalArgumentException(ERROR_PAID_FEE); | ||
| } | ||
| if (!isPaid && fee != 0) { | ||
| throw new IllegalArgumentException(ERROR_FREE_FEE); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package nextstep.sessions.domain; | ||
|
|
||
| public enum SessionStatus { | ||
| PREPARING, | ||
| OPEN, | ||
| CLOSED | ||
| } |
46 changes: 46 additions & 0 deletions
46
src/main/java/nextstep/sessions/domain/enrollment/Capacity.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package nextstep.sessions.domain.enrollment; | ||
|
|
||
| public class Capacity { | ||
|
|
||
| private static final String ERROR_MAX_CAPACITY_REQUIRED = "유료 강의는 최대 수강인원이 있어야 합니다"; | ||
|
|
||
| private final Integer maxCapacity; | ||
| private final boolean unlimited; | ||
|
|
||
| Capacity(Integer maxCapacity, boolean unlimited) { | ||
| validateMaxCapacity(maxCapacity); | ||
| this.maxCapacity = maxCapacity; | ||
| this.unlimited = unlimited; | ||
| } | ||
|
|
||
| public static Capacity limited(int maxCapacity) { | ||
| return new Capacity(maxCapacity, false); | ||
| } | ||
|
|
||
| public static Capacity unlimited() { | ||
| return new Capacity(Integer.MAX_VALUE, true); | ||
| } | ||
|
|
||
| public Integer maxCapacity() { | ||
| return maxCapacity; | ||
| } | ||
|
|
||
| public boolean canEnroll(int enrollmentsSize) { | ||
| return unlimited || enrollmentsSize < maxCapacity; | ||
| } | ||
|
|
||
| public boolean isUnlimited() { | ||
| return unlimited; | ||
| } | ||
|
|
||
| public boolean isFull(int enrollmentsSize) { | ||
| return enrollmentsSize >= maxCapacity; | ||
| } | ||
|
|
||
| private void validateMaxCapacity(Integer maxCapacity) { | ||
| if (!unlimited && (maxCapacity == null || maxCapacity <= 0)) { | ||
| throw new IllegalArgumentException(ERROR_MAX_CAPACITY_REQUIRED); | ||
| } | ||
| } | ||
|
|
||
| } |
57 changes: 57 additions & 0 deletions
57
src/main/java/nextstep/sessions/domain/enrollment/Enrollment.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package nextstep.sessions.domain.enrollment; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.Objects; | ||
| import nextstep.payments.domain.Payment; | ||
| import nextstep.sessions.domain.SessionPricing; | ||
| import nextstep.users.domain.NsUser; | ||
|
|
||
| public class Enrollment { | ||
|
|
||
| static final String ERROR_USER_PAYMENT_MISMATCH = "결제한 사용자와 신청자가 일치하지 않습니다"; | ||
|
|
||
| private final NsUser user; | ||
| private final Payment payment; | ||
| private final LocalDateTime enrolledAt; | ||
|
|
||
| public Enrollment(NsUser user, Payment payment) { | ||
| validateUserPayMatch(user, payment); | ||
| this.user = user; | ||
| this.payment = payment; | ||
| this.enrolledAt = LocalDateTime.now(); | ||
| } | ||
|
|
||
| public Payment payment() { | ||
| return payment; | ||
| } | ||
|
|
||
| public boolean canPayFor(SessionPricing pricing) { | ||
| return !pricing.isPaid() || payment.isPaidFor(pricing.fee()); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| Enrollment that = (Enrollment) o; | ||
| return Objects.equals(user, that.user) && Objects.equals(payment, that.payment) | ||
| && Objects.equals(enrolledAt, that.enrolledAt); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(user, payment, enrolledAt); | ||
| } | ||
|
|
||
| private static void validateUserPayMatch(NsUser user, Payment payment) { | ||
| if (payment.isPaidBy(user)) { | ||
| return; | ||
| } | ||
| throw new IllegalArgumentException(ERROR_USER_PAYMENT_MISMATCH); | ||
| } | ||
|
|
||
| } |
23 changes: 23 additions & 0 deletions
23
src/main/java/nextstep/sessions/domain/enrollment/Enrollments.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package nextstep.sessions.domain.enrollment; | ||
|
|
||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
|
|
||
| public class Enrollments { | ||
|
|
||
| static final String ERROR_ALREADY_ENROLLED = "이미 등록된 사용자입니다."; | ||
|
|
||
| private final Set<Enrollment> enrollments = new HashSet<>(); | ||
|
|
||
| public void add(Enrollment enrollment) { | ||
| if (enrollments.contains(enrollment)) { | ||
| throw new IllegalArgumentException(ERROR_ALREADY_ENROLLED); | ||
| } | ||
| enrollments.add(enrollment); | ||
| } | ||
|
|
||
| public int size() { | ||
| return enrollments.size(); | ||
| } | ||
| } | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Session의 필드 수가 많다.
Session의 기본 정보를 가지는 필드(현재 SessionInfo)와 수강신청에 관여하는 필드 값을 하나의 객체로 묶은 후 수강 신청 로직을 이 객체가 담당하도록 역할을 부여하는 것은 어떨까?