Skip to main content

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

PathRole
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/adapter/in/webPublic lecture and student lecture controllers
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/adapter/in/internal/LectureServiceAdapter.javaInternal monolith API implementation
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/application/serviceCommand and query services
lumie-backend/modules/lecture/src/main/java/com/lumie/lecture/domain/entityLecture and LectureClassTarget
lumie-backend/libs/internal-api/src/main/java/com/lumie/lecture/api/LectureService.javaInternal lecture lookup contract
lumie-backend/app/src/main/resources/db/migration/public/V18__rls_baseline.sqlBaseline lectures table
lumie-backend/app/src/main/resources/db/migration/public/V57__create_lecture_class_targets.sqlIntroduces lecture_class_targets and documents the cross-module no-FK boundary

Public Surface

SurfaceEntrypoints
Lecture CRUDPOST /v1/lectures, GET /v1/lectures, GET /v1/lectures/{id}, PATCH /v1/lectures/{id}, DELETE /v1/lectures/{id}
Student listingGET /v1/lectures/student/class/{classId}
Student watch payloadGET /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 classId and classIds.

The duplicated classId and classIds fields reflect the module's current storage model, not two separate business concepts.

Aggregates And Tables

AggregateTableNotes
LecturelecturesStores teacher, title, description, YouTube URL, visibility, lecture date, and a legacy primary class_id
LectureClassTargetlecture_class_targetsStores 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.STUDENT requires at least one class target.
  • Student lecture listing uses lectureRepository.findVisibleByTargetClassId(...), so target rows, not just lectures.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_id is maintained as the primary or fallback class and lecture_class_targets stores the full set.

Dependencies And Boundaries

DependencyWhy it exists
StaffServiceResolve the current instructor staff ID from the authenticated user
ClassServiceValidate class targets and student enrollment visibility
StudentServiceResolve the current student ID from the authenticated user for watch access

Failure Modes

  • Unknown lectures or classes fail with LECTURE_NOT_FOUND or CLASS_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_id still exists and is used as the primary or fallback class;
  • lecture_class_targets is 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, and LectureResponseTest pass without failures.
  • Docusaurus finishes without MDX or broken-link errors for backend/lecture-svc.