Lecture Service
This page covers lumie-backend/modules/lecture.
Responsibility
The lecture module owns:
- lecture records and metadata;
- lecture visibility state;
- lecture-to-class targeting for student access;
- student-visible lecture listings and watch payloads;
- a small internal API for lecture lookup by lecture ID or class.
Source Paths
| Path | Role |
|---|---|
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/adapter/in/web | Public lecture and student lecture controllers |
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/adapter/in/internal/LectureServiceAdapter.java | Internal monolith API implementation |
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/application/service | Command and query services |
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/domain/entity | Lecture and LectureClassTarget |
lumie-backend/libs/internal-api/src/main/java/com/lumie/lecture/api/LectureService.java | Internal lecture lookup contract |
lumie-backend/app/src/main/resources/db/migration/public/V18__rls_baseline.sql | Baseline lectures table |
lumie-backend/app/src/main/resources/db/migration/public/V57__create_lecture_class_targets.sql | Introduces lecture_class_targets and documents the cross-module no-FK boundary |
Public Surface
| Surface | Entrypoints |
|---|---|
| Lecture CRUD | POST /v1/lectures, GET /v1/lectures, GET /v1/lectures/{id}, PATCH /v1/lectures/{id}, DELETE /v1/lectures/{id} |
| Student listing | GET /v1/lectures/student/class/{classId} |
| Student watch payload | GET /v1/lectures/student/{id}/watch |
Internal Surface
LectureService exports:
- lookup of a single lecture by ID;
- paged lookup of lectures by class;
- a response shape that includes both
classIdandclassIds.
The duplicated classId and classIds fields reflect the module's current storage model, not two separate business concepts.
Aggregates And Tables
| Aggregate | Table | Notes |
|---|---|---|
Lecture | lectures | Stores teacher, title, description, YouTube URL, visibility, lecture date, and a legacy primary class_id |
LectureClassTarget | lecture_class_targets | Stores every student-visible class target for a lecture |
The migration comment in V57__create_lecture_class_targets.sql is the key schema boundary: class_id refers to the class module semantically, but the database intentionally does not enforce a foreign key because lecture and classroom are separate module boundaries.
Runtime Flow
Student-visible lecture access
Key Behaviors
- Instructor users can manage only lectures for classes assigned to them.
LectureVisibility.STUDENTrequires at least one class target.- Student lecture listing uses
lectureRepository.findVisibleByTargetClassId(...), so target rows, not justlectures.class_id, control visibility. - Student watch access checks both:
- the lecture is student-visible;
- the current student is enrolled in at least one targeted class.
- On write,
class_idis maintained as the primary or fallback class andlecture_class_targetsstores the full set.
Dependencies And Boundaries
| Dependency | Why it exists |
|---|---|
StaffService | Resolve the current instructor staff ID from the authenticated user |
ClassService | Validate class targets and student enrollment visibility |
StudentService | Resolve the current student ID from the authenticated user for watch access |
Failure Modes
- Unknown lectures or classes fail with
LECTURE_NOT_FOUNDorCLASS_NOT_FOUND. - Student visibility without targets fails with
STUDENT_VISIBILITY_REQUIRES_CLASS. - Students outside the target class set receive
LECTURE_NOT_VISIBLE_TO_CLASS. - Instructors who try to manage lectures outside their assigned classes receive
FORBIDDEN.
Known Contract Nuance
Lecture targeting is in a migration state by design:
lectures.class_idstill exists and is used as the primary or fallback class;lecture_class_targetsis the authoritative multi-class visibility table for student access.
The docs describe both because the current code reads and writes both.
Representative Contract Example
This example matches CreateLectureRequest, LectureResponse, and LectureCommandServiceTest.create_deduplicatesAndStoresClassTargets.
POST /v1/lectures
{
"classId": 2,
"classIds": [2, 3],
"teacherId": 10,
"title": "수업 영상",
"description": null,
"youtubeUrl": "https://www.youtube.com/watch?v=abc123",
"visibility": "STUDENT",
"lectureDate": null
}
{
"id": 1,
"classId": 2,
"classIds": [2, 3],
"teacherId": 10,
"title": "수업 영상",
"description": null,
"youtubeUrl": "https://www.youtube.com/watch?v=abc123",
"visibility": "STUDENT",
"lectureDate": null,
"createdAt": "2026-06-14T09:00:00Z",
"updatedAt": "2026-06-14T09:00:00Z"
}
classId remains the primary or fallback class stored on lectures, while classIds is the deduplicated full target set persisted in lecture_class_targets. Student watch access is evaluated against that target set, not just the legacy classId column.
Observability
- Command and query services log lifecycle events and rely on synchronous validation.
- There is no queue, retry loop, or worker interaction in this module.
Verification
./gradlew :modules:lecture:test
./gradlew :app:test --tests '*Lecture*'
cd lumie-document/docusaurus && npm run build
Expected success signals:
- Gradle finishes with
BUILD SUCCESSFUL. LectureCommandServiceTest,LectureQueryServiceTest, andLectureResponseTestpass without failures.- Docusaurus finishes without MDX or broken-link errors for
backend/lecture-svc.