Tenant Schema
Purpose
Tenant domain tables model academy operations inside a single tenant. This ERD shows the product relationships that most often matter when changing backend modules, worker callbacks, or frontend data assumptions.
Source Paths
| Path | Role |
|---|---|
lumie-backend/app/src/main/resources/db/migration/public/V18__rls_baseline.sql | Baseline tenant tables and foreign keys |
lumie-backend/app/src/main/resources/db/migration/public/V25__drop_omr_grading_jobs_results.sql | Removes the dead omr_grading_jobs.results column |
lumie-backend/app/src/main/resources/db/migration/public/V26__rename_admin_tables_to_staff.sql | Admin-to-staff terminology migration |
lumie-backend/app/src/main/resources/db/migration/public/V40__omr_grading_jobs_add_version.sql | Adds the missing optimistic-lock version column to omr_grading_jobs |
lumie-backend/app/src/main/resources/db/migration/public/V41__exam_results_add_omr_grading_job_id.sql | Links exam_results rows back to the producing OMR job |
lumie-backend/app/src/main/resources/db/migration/public/V54__create_file_links.sql | Tenant-safe file link model |
lumie-backend/app/src/main/resources/db/migration/public/V55__create_announcement_class_targets.sql | Announcement-to-class targeting |
lumie-backend/app/src/main/resources/db/migration/public/V57__create_lecture_class_targets.sql | Lecture-to-class targeting |
lumie-backend/app/src/main/resources/db/migration/public/V62__add_read_receipt_tables.sql | Read receipt tables |
lumie-backend/app/src/main/resources/db/migration/public/V65__repair_read_receipt_and_file_download_rls.sql | RLS repair migration |
lumie-backend/modules/exam/src/main/java/com/lumie/exam/domain/entity/OmrGradingJob.java | Current JPA mapping for omr_grading_jobs |
lumie-backend/modules/exam/src/main/java/com/lumie/exam/domain/entity/ReportGenerationJob.java | Current JPA mapping for report_generation_jobs |
lumie-backend/libs/common/src/main/java/com/lumie/common/domain/TenantScopedEntity.java | Inherited tenant_id, created_at, and updated_at contract |
Core Tenant ERD
Exam And Worker ERD
tenant_id is inherited from TenantScopedEntity; tenant_slug is stored
explicitly on both job rows for downstream correlation. The current schema does
not contain a request_id column on either job table, so the ERD stays aligned
with the real fields used by the backend.
Worker services do not own these product tables. The backend creates job rows, publishes work messages, and records callbacks or result state through backend-owned services so tenant context and exam ownership remain centralized.
Content And File ERD
file_links and file_download are tenant-safe attachment tables. When a
relation can cross module boundaries, the migration must preserve the
tenant_id guard and document whether the relation is hard FK or soft reference.
Verification
cd /path/to/Lumie
rg -n "CREATE TABLE omr_grading_jobs|CREATE TABLE report_generation_jobs|DROP COLUMN IF EXISTS results|ADD COLUMN version" \
lumie-backend/app/src/main/resources/db/migration/public/V18__rls_baseline.sql \
lumie-backend/app/src/main/resources/db/migration/public/V25__drop_omr_grading_jobs_results.sql \
lumie-backend/app/src/main/resources/db/migration/public/V40__omr_grading_jobs_add_version.sql
Expected success signal: the baseline create statements appear, results is
dropped in V25, and only omr_grading_jobs receives a later version column.
cd /path/to/Lumie
rg -n "class OmrGradingJob|class ReportGenerationJob|tenantSlug|zipFileKey|savedCount|studentIds" \
lumie-backend/modules/exam/src/main/java/com/lumie/exam/domain/entity
Expected success signal: OmrGradingJob exposes savedCount and imageKeys,
while ReportGenerationJob exposes studentIds and zipFileKey.
Review Checklist
- Does every table in the relation carry the same tenant boundary?
- Does the migration keep
tenant_idin join tables? - Does a unique constraint need
tenant_idto avoid cross-tenant collisions? - Is a soft reference more appropriate than a hard FK across module ownership?
- Do worker callbacks go through backend-owned tenant-aware APIs?