Enrollment: Where the LMS Starts Feeling Like a Real Product | LMS Series | Project #15
hosiyar
0:00 / 0:00
Enrollment: Where the LMS Starts Feeling Like a Real Product | LMS Series | Project #15
145 просмотров · 9 дней назад
hosiyar
692 подписчика
145 просмотров · 9 дней назад
The first entity in this series whose only job is to link two other modules together — and the purest example yet of the pattern we committed to back in Module 2.
This is Module 3, Chapter 2 of the Spring Boot LMS series — the first code of the module, building directly on Chapter 1's design and everything from Modules 1 and 2.
In this video:
✅ The Enrollment entity — two plain UUIDs (studentId, courseId), no foreign key to either, because both ids belong to other modules. The entity exists purely to link them
✅ The migration — no foreign keys, plus a unique constraint on (student_id, course_id) that makes "can't enroll twice" a database guarantee, not an application hope
✅ The course module grows its own public interface — until now, only the user module had one (UserDirectory). Enrollment needs to ask "does this course exist and is it published," so the course module gains CourseDirectory + CourseSummary, mirroring the existing pattern
✅ The teaching point on timing — the interface appears exactly when a real cross-module need arrives, not speculatively, two modules earlier
✅ Enrolling via POST /enrollments with a body of just {courseId} — student identity always from the token, never the body; published courses only, checked via CourseDirectory, with drafts and non-existent ids both returning 404
✅ A deliberate callback to Video 3's registration flow — the enroll-twice race condition, solved the same two-layer way: a cheap existsByStudentIdAndCourseId check for the common case, plus a try/catch on DataIntegrityViolationException for the concurrent case where two requests both pass the check and the unique constraint admits only one
✅ The framing that ties it together: an application check is a courtesy, the database constraint is the guarantee
✅ "My enrollments" (GET /enrollments/me) — token-scoped, paginated, with a batched CourseDirectory.findAllByIds lookup for course titles, reusing the N+1 guard from Module 2 unchanged
✅ Unenrolling, and what it deliberately does not do — removes the enrollment row, leaves progress completely untouched. Progress isn't built yet, but this is stated clearly on camera because Chapters 4 and 5 depend on it
✅ A short note on security — all enrollment endpoints are simply authenticated(), already covered by the existing catch-all rule. Unlike courses, any logged-in user can enroll, so there's no access distinction to make — the absence of a rule is itself a decision
✅ A seven-check Postman demo — enroll, duplicate rejection, draft rejection, nonexistent course, the scoped list, unenroll, and unenrolling something you don't have
✅ Interview Prep — why both a check and a constraint, why no foreign keys on a join table, and when a module should expose a public interface
🛠️ Tools used in this video:
Java 21 | Spring Boot 4.1 (Spring Framework 7) | Spring Data JPA | Flyway | MySQL | Postman | IntelliJ IDEA
📁 GitHub Repo: https://github.com/deep473/hosiyar-sp...
#SpringBoot #Java21 #ModularMonolith #SpringDataJPA #SoftwareArchitecture #SpringBootTutorial #BackendDevelopment #SystemDesign #RESTAPI #LMSSeries