Skip to content

LMS data model

Audience: backend
Status: specced
Owns: backend
Depends on: LMS overview, API overview, Orchestrator data model, Coding standards

Design SoT: ecosystem-root lms-schema.prisma. This page is the human outline. Not yet migratedCHECK constraints on String status fields must be hand-added to migration SQL (same convention as CAP).

Rule Detail
ULID PKs @id @db.Char(26), app-generated
Money BigInt minor units on Course — never Float
JSONB Onboarding data, item body / quiz, completion policy, sequencing, CMI snapshot
Enums Structural only (LmsPersonaType, ItemType, EntitlementSource, StaffRole, …)
Status fields String + intended CHECK
Cross-service ids sectorId / tradeId / unitIds / paymentId / userId / assetId are strings, not FKs

LmsUser — identity anti-corruption layer

Section titled “LmsUser — identity anti-corruption layer”
Pattern Rule
One table holds Orchestrator userId LmsUser.userId unique + indexed
All other LMS tables FK to LmsUser.id Not to Orchestrator ids directly
Provisioning Consume user.created idempotently (dedupe on userId); lazy-create on first authenticated LMS hit is allowed
Model Notes
LmsUser Local id + OL userId
OnboardingRecord Unique (lmsUserId, persona); JSONB data + schemaVersion
LearnerProfile, InstructorProfile, StaffMembership Created on onboarding submit
Invite Instructor or staff; email + status
Course Price, status, optional CAP linkage, completionPolicy JSON, sequencing JSON
CourseModule, CourseItem Item types include SCORM_PACKAGE (packageAssetId)
CourseInstructor OWNER / CO_INSTRUCTOR
Entitlement Source + status; optional sponsor / paymentId / capApplicationId
Enrollment Unique (courseId, learnerLmsUserId); percentComplete
ItemProgress Per item status + optional CMI JSON
AssessmentAttempt Native quizzes only
ScormRuntimeSession LMS launch path (extracted files), not Cloudinary URL
LmsCertificate 1:1 enrollment when issued; OL assetId
OutboxEvent Transactional outbox
  • Entitlement = why the learner may access.
  • Enrollment = progress container.
  • Player requires entitlement ACTIVE and enrollment ACTIVE. Do not put hasPaid on Course or LmsUser.

Canonical zip bytes: Orchestrator StorageAsset (packageAssetId). Extracted player tree: LMS-local (ScormRuntimeSession.launchPath or equivalent object-store prefix). Future OL package adapter does not change LMS FKs — still an assetId.