Skip to main content

Data Model

Status: Living document — update whenever entities or attributes change. Last updated: 2026-02-18

This document defines the canonical entity model for Curriculab. It is the authoritative reference for database schema design, API contract design, and code generation.


1. Conventions

ConventionRule
Primary keysAll PKs are uuid (v4) unless noted
TimestampsAll timestamps are timestamptz stored in UTC
Implied columnscreated_at timestamptz NOT NULL DEFAULT now() and updated_at timestamptz NOT NULL DEFAULT now() are present on every entity and omitted from entity tables to reduce noise
NamingAttributes use snake_case; entity names use PascalCase
Soft deletesEntities use a status enum (e.g., active/inactive/archived) rather than physical deletion
Calculated fieldsFields marked [calculated] are derived at the application layer or via a DB trigger/materialized view; they are not raw stored columns unless noted
EnumsInline as enum(val1, val2, …) in the Type column
Arraystext[] denotes a PostgreSQL array of strings
JSON blobsjsonb for semi-structured or variable-shape data
Cross-cluster FKsShown in entity tables with a cluster reference in the Notes column (e.g., → Cluster 1: User)

2. Domain Overview

The six domain clusters and their primary dependency arrows. Arrows point from the cluster that is depended on to the cluster that depends on it.

flowchart TD
IA["**Cluster 1**\nIdentity & Access\n_(User, Session, …)_"]
OS["**Cluster 2**\nOrganizational Structure\n& Curriculum\n_(College, Department, Course, …)_"]
AC["**Cluster 3**\nAcademic Calendar\n& Scheduling\n_(Term, Section, …)_"]
SP["**Cluster 4**\nStudent Profile\n& Registration\n_(Student, Enrollment, …)_"]
AR["**Cluster 5**\nAdvising, Records\n& Graduation\n_(AdvisorAssignment, Transcript, …)_"]
BL["**Cluster 6**\nBilling, Transfer Credit,\nDocuments & Communications\n_(Charge, Payment, Notification, …)_"]

IA --> OS
IA --> AC
IA --> SP
OS --> AC
OS --> SP
OS --> AR
AC --> SP
SP --> AR
SP --> BL
AR --> BL
IA --> BL
OS --> BL

3. Entity Catalog


Cluster 1 — Identity & Access

Entities: User · Session · TwoFactorEnrollment · PasswordResetToken · AuditLog · ApiConsumer

erDiagram
User ||--o{ Session : "has"
User ||--o| TwoFactorEnrollment : "enrolls in"
User ||--o{ PasswordResetToken : "requests"
User ||--o{ AuditLog : "generates"
User ||--o{ ApiConsumer : "owns"

User

AttributeTypeConstraintsNotes
iduuidPK
emailtextNOT NULL, UNIQUE
password_hashtextnullableNull for SSO-only accounts
roleenum(student, instructor, advisor, academic_staff, registrar, administrator)NOT NULLSingle role per user; RBAC enforced at application layer
statusenum(active, inactive, suspended, pending)NOT NULL, DEFAULT 'pending'
first_nametextNOT NULL
last_nametextNOT NULL
phonetextnullable
sso_providertextnullablee.g., google, saml
sso_subjecttextnullableExternal identity provider subject ID
last_login_attimestamptznullable

Session

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidNOT NULL, FK → User
token_hashtextNOT NULL, UNIQUESHA-256 of the bearer token
ip_addressinetnullable
user_agenttextnullable
expires_attimestamptzNOT NULL
revoked_attimestamptznullableNull while active

TwoFactorEnrollment

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidNOT NULL, FK → User, UNIQUEOne enrollment per user
methodenum(totp, sms, email)NOT NULL
secret_encryptedtextNOT NULLAES-256 encrypted TOTP secret or phone/email
phonetextnullableRequired when method = sms
verifiedbooleanNOT NULL, DEFAULT false
enrolled_attimestamptznullableSet when verified = true

PasswordResetToken

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidNOT NULL, FK → User
token_hashtextNOT NULL, UNIQUESHA-256 of the one-time token
expires_attimestamptzNOT NULL
used_attimestamptznullableNull until redeemed

AuditLog

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidnullable, FK → UserNull for system-generated events
actiontextNOT NULLe.g., user.login, enrollment.create
entity_typetextnullablee.g., Enrollment
entity_iduuidnullablePK of the affected record
ip_addressinetnullable
user_agenttextnullable
metadatajsonbnullableAdditional context (old/new values, request body, etc.)
occurred_attimestamptzNOT NULL, DEFAULT now()Dedicated field; complements created_at

AuditLog is append-only — no updates or deletes permitted.


ApiConsumer

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULLHuman-readable label for the consumer
client_idtextNOT NULL, UNIQUEOAuth 2.0 client identifier
client_secret_hashtextNOT NULLBcrypt hash of the client secret
scopestext[]NOT NULL, DEFAULT ''Allowed OAuth scopes
owner_user_iduuidnullable, FK → UserStaff member responsible for this consumer
statusenum(active, revoked)NOT NULL, DEFAULT 'active'

Cluster 2 — Organizational Structure & Curriculum

Entities: College · Department · Building · Room · RoomBlock · Program · RequirementCategory · RequirementSlot · Course · CoursePrerequisite

erDiagram
College ||--|{ Department : "contains"
Department ||--o{ Program : "offers"
Department ||--o{ Course : "owns"
Program ||--|{ RequirementCategory : "has"
RequirementCategory ||--|{ RequirementSlot : "contains"
RequirementSlot }o--o| Course : "maps to"
Course ||--o{ CoursePrerequisite : "requires"
CoursePrerequisite }o--|| Course : "is prerequisite for"
Building ||--|{ Room : "contains"
Room ||--o{ RoomBlock : "blocked by"

College

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULL, UNIQUE
codetextNOT NULL, UNIQUEe.g., ENG
dean_user_iduuidnullable, FK → User→ Cluster 1: User
statusenum(active, inactive)NOT NULL, DEFAULT 'active'

Department

AttributeTypeConstraintsNotes
iduuidPK
college_iduuidNOT NULL, FK → College
nametextNOT NULL
codetextNOT NULL, UNIQUEe.g., CS
chair_user_iduuidnullable, FK → User→ Cluster 1: User
statusenum(active, inactive)NOT NULL, DEFAULT 'active'

Building

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULL
codetextNOT NULL, UNIQUEe.g., SCI
addresstextnullable
campustextnullableFor multi-campus institutions

Room

AttributeTypeConstraintsNotes
iduuidPK
building_iduuidNOT NULL, FK → Building
room_numbertextNOT NULL
capacityintegerNOT NULL, CHECK > 0
room_typeenum(classroom, lab, lecture_hall, seminar, other)NOT NULL
featurestext[]NOT NULL, DEFAULT ''e.g., ['projector','whiteboard']
statusenum(active, inactive)NOT NULL, DEFAULT 'active'

Unique constraint on (building_id, room_number).


RoomBlock

AttributeTypeConstraintsNotes
iduuidPK
room_iduuidNOT NULL, FK → Room
start_attimestamptzNOT NULL
end_attimestamptzNOT NULLCHECK end_at > start_at
reasontextnullable
created_byuuidNOT NULL, FK → User→ Cluster 1: User

Program

AttributeTypeConstraintsNotes
iduuidPK
department_iduuidNOT NULL, FK → Department
nametextNOT NULL
codetextNOT NULLe.g., CS-BS
degree_typeenum(certificate, associate, bachelor, master, doctoral)NOT NULL
catalog_yearintegerNOT NULLFour-digit year
total_credits_requiredintegerNOT NULL, CHECK > 0
statusenum(active, inactive, retired)NOT NULL, DEFAULT 'active'

Unique constraint on (code, catalog_year).


RequirementCategory

AttributeTypeConstraintsNotes
iduuidPK
program_iduuidNOT NULL, FK → Program
nametextNOT NULLe.g., Core Requirements
min_creditsintegerNOT NULL, DEFAULT 0
max_creditsintegernullableNull = no upper limit
sort_orderintegerNOT NULL, DEFAULT 0Display ordering

RequirementSlot

AttributeTypeConstraintsNotes
iduuidPK
category_iduuidNOT NULL, FK → RequirementCategory
course_iduuidnullable, FK → CourseNull when is_elective = true
is_electivebooleanNOT NULL, DEFAULT falseElective slots have no fixed course
min_gradetextnullablee.g., C
notestextnullable

Course

AttributeTypeConstraintsNotes
iduuidPK
department_iduuidNOT NULL, FK → Department
codetextNOT NULLe.g., CS101
titletextNOT NULL
creditsnumeric(4,2)NOT NULL, CHECK > 0
descriptiontextnullable
delivery_modeenum(in_person, online, hybrid)NOT NULL, DEFAULT 'in_person'Default mode; sections may override
statusenum(active, inactive, retired)NOT NULL, DEFAULT 'active'

Unique constraint on (department_id, code).


CoursePrerequisite

AttributeTypeConstraintsNotes
iduuidPK
course_iduuidNOT NULL, FK → CourseThe course that has this prerequisite
prerequisite_course_iduuidNOT NULL, FK → CourseThe required prior course
min_gradetextnullableMinimum passing grade required
is_concurrent_allowedbooleanNOT NULL, DEFAULT falseTrue = co-requisite (may be taken simultaneously)

Unique constraint on (course_id, prerequisite_course_id). CHECK course_id <> prerequisite_course_id.


Cluster 3 — Academic Calendar & Scheduling

Entities: Term · ProgramDeadline · Section · SectionMeetingTime · ReservedSeatGroup

erDiagram
Term ||--o{ ProgramDeadline : "has"
Term ||--o{ Section : "hosts"
Section ||--o{ SectionMeetingTime : "meets at"
Section ||--o{ ReservedSeatGroup : "reserves seats via"

Term

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULLe.g., Fall 2026
codetextNOT NULL, UNIQUEe.g., 2026FA
academic_yearintegerNOT NULLe.g., 2026
term_typeenum(fall, spring, summer, winter)NOT NULL
start_datedateNOT NULL
end_datedateNOT NULLCHECK end_date > start_date
registration_open_attimestamptzNOT NULL
registration_close_attimestamptzNOT NULL
add_drop_deadlinedateNOT NULL
withdrawal_deadlinedateNOT NULL
grade_submission_deadlinedateNOT NULL
statusenum(upcoming, active, closed, archived)NOT NULL, DEFAULT 'upcoming'

ProgramDeadline

AttributeTypeConstraintsNotes
iduuidPK
term_iduuidNOT NULL, FK → Term
program_iduuidnullable, FK → Program→ Cluster 2: Program; null = applies to all programs
deadline_typetextNOT NULLe.g., graduation_application, add_drop
deadline_attimestamptzNOT NULL
notestextnullable

Section

AttributeTypeConstraintsNotes
iduuidPK
course_iduuidNOT NULL, FK → Course→ Cluster 2: Course
term_iduuidNOT NULL, FK → Term
section_numbertextNOT NULLe.g., 001, 002
instructor_user_iduuidnullable, FK → User→ Cluster 1: User
room_iduuidnullable, FK → Room→ Cluster 2: Room
delivery_modeenum(in_person, online, hybrid)NOT NULLOverrides course-level default
capacityintegerNOT NULL, CHECK > 0
enrolled_countinteger[calculated]Derived from active Enrollments
waitlist_countinteger[calculated]Derived from active Waitlist entries
statusenum(open, closed, cancelled, waitlist_only)NOT NULL, DEFAULT 'open'

Unique constraint on (course_id, term_id, section_number).


SectionMeetingTime

AttributeTypeConstraintsNotes
iduuidPK
section_iduuidNOT NULL, FK → Section
day_of_weekenum(mon, tue, wed, thu, fri, sat, sun)NOT NULL
start_timetimeNOT NULLTime-of-day without date
end_timetimeNOT NULLCHECK end_time > start_time

ReservedSeatGroup

AttributeTypeConstraintsNotes
iduuidPK
section_iduuidNOT NULL, FK → Section
nametextNOT NULLe.g., Honors Students
seats_reservedintegerNOT NULL, CHECK > 0
criteriajsonbNOT NULL, DEFAULT ''Eligibility rules (e.g., program, standing)
priority_orderintegerNOT NULL, DEFAULT 0Lower = higher priority

Cluster 4 — Student Profile & Registration

Entities: Student · EmergencyContact · StudentProgram · Enrollment · GradeCorrection · Waitlist · Hold · CourseChangeHistory

erDiagram
Student ||--o{ EmergencyContact : "has"
Student ||--o{ StudentProgram : "enrolled in"
Student ||--o{ Enrollment : "creates"
Student ||--o{ Waitlist : "joins"
Student ||--o{ Hold : "subject to"
Enrollment ||--o{ GradeCorrection : "corrected by"
Section ||--o{ Enrollment : "has"
Section ||--o{ Waitlist : "has"
Section ||--o{ CourseChangeHistory : "tracks"

Student

AttributeTypeConstraintsNotes
iduuidPK, FK → UserJoined-table inheritance; shares PK with User
student_numbertextNOT NULL, UNIQUEInstitutional student ID
cumulative_gpanumeric(4,3)[calculated]Derived from completed Enrollments
total_credit_hours_completednumeric(6,2)[calculated]Sum of credits for completed courses
enrollment_statusenum(enrolled, part_time, leave_of_absence, graduated, withdrawn)NOT NULL, DEFAULT 'enrolled'
photo_urltextnullable
admit_datedateNOT NULL

EmergencyContact

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student
nametextNOT NULL
relationshiptextNOT NULLe.g., Parent, Spouse
phonetextNOT NULL
emailtextnullable
is_primarybooleanNOT NULL, DEFAULT false

StudentProgram

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student
program_iduuidNOT NULL, FK → Program→ Cluster 2: Program
catalog_yearintegerNOT NULLCatalog year under which the student was admitted
declared_attimestamptzNOT NULL
statusenum(active, completed, withdrawn)NOT NULL, DEFAULT 'active'
is_primarybooleanNOT NULL, DEFAULT falseOne primary program per student (enforced at app layer)

Enrollment

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student
section_iduuidNOT NULL, FK → Section→ Cluster 3: Section
enrolled_attimestamptzNOT NULL, DEFAULT now()
gradetextnullablee.g., A, B+, W; null until grade is posted
grade_pointsnumeric(4,3)nullableQuality points for GPA calculation
credits_attemptednumeric(4,2)NOT NULLCopy of Section→Course.credits at enrollment time
completion_statusenum(enrolled, withdrawn, completed, incomplete, auditing)NOT NULL, DEFAULT 'enrolled'
is_repeatingbooleanNOT NULL, DEFAULT falseTrue if student previously attempted this course

Unique constraint on (student_id, section_id).


GradeCorrection

AttributeTypeConstraintsNotes
iduuidPK
enrollment_iduuidNOT NULL, FK → Enrollment
old_gradetextNOT NULLThe grade being replaced
new_gradetextNOT NULLThe replacement grade
reasontextNOT NULL
submitted_byuuidNOT NULL, FK → User→ Cluster 1: User
approved_byuuidnullable, FK → User→ Cluster 1: User
submitted_attimestamptzNOT NULL, DEFAULT now()
approved_attimestamptznullable
statusenum(pending, approved, rejected)NOT NULL, DEFAULT 'pending'

Waitlist

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student
section_iduuidNOT NULL, FK → Section→ Cluster 3: Section
positionintegerNOT NULLManaged by application; 1 = next in line
joined_attimestamptzNOT NULL, DEFAULT now()
notified_attimestamptznullableWhen seat-offer notification was sent
expires_attimestamptznullableDeadline to accept an offered seat
statusenum(waiting, offered, declined, expired, enrolled)NOT NULL, DEFAULT 'waiting'

Unique constraint on (student_id, section_id).


Hold

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student
hold_typeenum(financial, academic, administrative, disciplinary)NOT NULL
descriptiontextNOT NULL
placed_byuuidNOT NULL, FK → User→ Cluster 1: User
placed_attimestamptzNOT NULL, DEFAULT now()
released_byuuidnullable, FK → User→ Cluster 1: User
released_attimestamptznullable
blocks_registrationbooleanNOT NULL, DEFAULT true

CourseChangeHistory

AttributeTypeConstraintsNotes
iduuidPK
section_iduuidNOT NULL, FK → Section→ Cluster 3: Section
changed_byuuidNOT NULL, FK → User→ Cluster 1: User
changed_attimestamptzNOT NULL, DEFAULT now()
field_nametextNOT NULLName of the changed attribute
old_valuetextnullableString representation of the prior value
new_valuetextnullableString representation of the new value

Cluster 5 — Advising, Records & Graduation

Entities: AdvisorAssignment · AdvisingAppointment · AdvisingNote · AcademicPlan · AcademicPlanEntry · TranscriptRequest · DegreeAuditSnapshot · GraduationApplication · Ceremony · CommencementRegistration · DiplomaOrder

erDiagram
Student ||--o{ AdvisorAssignment : "assigned to"
Student ||--o{ AdvisingAppointment : "books"
AdvisingAppointment ||--o{ AdvisingNote : "generates"
Student ||--o{ AcademicPlan : "has"
AcademicPlan ||--|{ AcademicPlanEntry : "contains"
Student ||--o{ TranscriptRequest : "requests"
Student ||--o{ DegreeAuditSnapshot : "has"
Student ||--o{ GraduationApplication : "submits"
GraduationApplication ||--o| DiplomaOrder : "leads to"
Ceremony ||--o{ CommencementRegistration : "has"
Student ||--o{ CommencementRegistration : "registers for"

AdvisorAssignment

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
advisor_user_iduuidNOT NULL, FK → User→ Cluster 1: User
assigned_attimestamptzNOT NULL, DEFAULT now()
unassigned_attimestamptznullableNull while assignment is active
is_primarybooleanNOT NULL, DEFAULT false

AdvisingAppointment

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
advisor_user_iduuidNOT NULL, FK → User→ Cluster 1: User
scheduled_attimestamptzNOT NULL
duration_minutesintegerNOT NULL, DEFAULT 30, CHECK > 0
locationtextnullablePhysical location or meeting URL
appointment_typeenum(in_person, virtual, phone)NOT NULL
statusenum(scheduled, completed, cancelled, no_show)NOT NULL, DEFAULT 'scheduled'
cancellation_reasontextnullable

AdvisingNote

AttributeTypeConstraintsNotes
iduuidPK
appointment_iduuidnullable, FK → AdvisingAppointmentNull for standalone notes
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
advisor_user_iduuidNOT NULL, FK → User→ Cluster 1: User
contenttextNOT NULL
is_privatebooleanNOT NULL, DEFAULT falsePrivate notes not visible to the student
note_datedateNOT NULL

AcademicPlan

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
advisor_user_iduuidnullable, FK → User→ Cluster 1: User; null if student-created
nametextNOT NULL
target_graduation_term_iduuidnullable, FK → Term→ Cluster 3: Term
statusenum(draft, active, completed)NOT NULL, DEFAULT 'draft'

AcademicPlanEntry

AttributeTypeConstraintsNotes
iduuidPK
plan_iduuidNOT NULL, FK → AcademicPlan
term_iduuidNOT NULL, FK → Term→ Cluster 3: Term
course_iduuidNOT NULL, FK → Course→ Cluster 2: Course
notetextnullable
statusenum(planned, registered, completed, dropped)NOT NULL, DEFAULT 'planned'

TranscriptRequest

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
request_typeenum(official, unofficial)NOT NULL
delivery_methodenum(pdf, paper, electronic)NOT NULL
recipient_nametextnullableFor third-party delivery
recipient_emailtextnullable
recipient_addresstextnullableMailing address for paper delivery
processing_feenumeric(8,2)NOT NULL, DEFAULT 0
payment_statusenum(unpaid, paid, waived)NOT NULL, DEFAULT 'unpaid'
statusenum(pending, processing, ready, delivered)NOT NULL, DEFAULT 'pending'
requested_attimestamptzNOT NULL, DEFAULT now()
delivered_attimestamptznullable

DegreeAuditSnapshot

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
program_iduuidNOT NULL, FK → Program→ Cluster 2: Program
snapshot_attimestamptzNOT NULL, DEFAULT now()
audit_datajsonbNOT NULLFull audit result; immutable after creation
is_graduation_clearancebooleanNOT NULL, DEFAULT falseTrue if used as the canonical graduation clearance
cleared_byuuidnullable, FK → User→ Cluster 1: User
cleared_attimestamptznullable

GraduationApplication

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
program_iduuidNOT NULL, FK → Program→ Cluster 2: Program
term_iduuidNOT NULL, FK → TermExpected graduation term; → Cluster 3: Term
applied_attimestamptzNOT NULL, DEFAULT now()
statusenum(submitted, under_review, approved, denied, withdrawn)NOT NULL, DEFAULT 'submitted'
reviewer_user_iduuidnullable, FK → User→ Cluster 1: User
reviewed_attimestamptznullable
notestextnullable

Ceremony

AttributeTypeConstraintsNotes
iduuidPK
term_iduuidNOT NULL, FK → Term→ Cluster 3: Term
nametextNOT NULLe.g., Spring 2026 Commencement
ceremony_datetimestamptzNOT NULL
locationtextNOT NULL
capacityintegerNOT NULL, CHECK > 0
statusenum(scheduled, completed, cancelled)NOT NULL, DEFAULT 'scheduled'

CommencementRegistration

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
ceremony_iduuidNOT NULL, FK → Ceremony
registered_attimestamptzNOT NULL, DEFAULT now()
guest_countintegerNOT NULL, DEFAULT 0, CHECK >= 0
accessibility_needstextnullable
statusenum(registered, cancelled)NOT NULL, DEFAULT 'registered'

Unique constraint on (student_id, ceremony_id).


DiplomaOrder

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
graduation_application_iduuidNOT NULL, FK → GraduationApplication
mailing_nametextNOT NULLName printed on diploma
mailing_addresstextNOT NULL
expeditedbooleanNOT NULL, DEFAULT false
feenumeric(8,2)NOT NULL, DEFAULT 0
payment_statusenum(unpaid, paid, waived)NOT NULL, DEFAULT 'unpaid'
ordered_attimestamptzNOT NULL, DEFAULT now()
shipped_attimestamptznullable
tracking_numbertextnullable
statusenum(pending, processing, shipped, delivered)NOT NULL, DEFAULT 'pending'

Cluster 6 — Billing, Transfer Credit, Documents & Communications

Entities: Charge · Payment · SavedPaymentMethod · FinancialAid · TransferCreditRequest · TransferCreditItem · TransferCreditAppeal · DocumentType · Document · GeneratedDocument · Notification · NotificationPreference · Announcement · SupportTicket · TicketMessage · HelpArticle · ScheduledReport

erDiagram
Student ||--o{ Charge : "incurs"
Student ||--o{ Payment : "makes"
Student ||--o{ SavedPaymentMethod : "stores"
Student ||--o{ FinancialAid : "receives"
Student ||--o{ TransferCreditRequest : "submits"
TransferCreditRequest ||--|{ TransferCreditItem : "contains"
TransferCreditItem ||--o{ TransferCreditAppeal : "appealed via"
DocumentType ||--o{ Document : "typed by"
DocumentType ||--o{ GeneratedDocument : "typed by"
Student ||--o{ Document : "has"
User ||--o{ Notification : "receives"
User ||--o{ NotificationPreference : "sets"
User ||--o{ Announcement : "authors"
User ||--o{ SupportTicket : "submits"
SupportTicket ||--|{ TicketMessage : "has"
User ||--o{ HelpArticle : "authors"
User ||--o{ ScheduledReport : "creates"

Charge

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
term_iduuidnullable, FK → Term→ Cluster 3: Term; null for non-term charges
charge_typeenum(tuition, fee, fine, other)NOT NULL
descriptiontextNOT NULL
amountnumeric(10,2)NOT NULL, CHECK > 0
due_datedatenullable
statusenum(pending, paid, waived, disputed)NOT NULL, DEFAULT 'pending'

Payment

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
amountnumeric(10,2)NOT NULL, CHECK > 0
payment_methodenum(credit_card, ach, check, financial_aid, other)NOT NULL
payment_datedateNOT NULL
reference_numbertextnullableGateway or check reference
statusenum(pending, completed, failed, refunded)NOT NULL, DEFAULT 'pending'
applied_chargesjsonbNOT NULL, DEFAULT '[]'Array of {charge_id, amount_applied}

SavedPaymentMethod

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
method_typeenum(credit_card, ach)NOT NULL
last_fourtextNOT NULLLast four digits of card or account number
card_brandtextnullablee.g., Visa, Mastercard; null for ACH
expiry_monthintegernullable1–12; null for ACH
expiry_yearintegernullableFour-digit year; null for ACH
billing_nametextNOT NULL
billing_addresstextnullable
provider_tokentextNOT NULLOpaque token from payment gateway; never raw PAN
is_defaultbooleanNOT NULL, DEFAULT false
statusenum(active, removed)NOT NULL, DEFAULT 'active'

FinancialAid

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
term_iduuidNOT NULL, FK → Term→ Cluster 3: Term
aid_typeenum(grant, loan, scholarship, work_study)NOT NULL
sourcetextNOT NULLe.g., Federal Pell, Institutional Merit
amount_awardednumeric(10,2)NOT NULL, CHECK >= 0
amount_disbursednumeric(10,2)NOT NULL, DEFAULT 0, CHECK >= 0
disbursement_datedatenullable
statusenum(pending, disbursed, cancelled)NOT NULL, DEFAULT 'pending'

TransferCreditRequest

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
institution_nametextNOT NULL
institution_countrytextNOT NULL, DEFAULT 'US'ISO 3166-1 alpha-2
submitted_attimestamptzNOT NULL, DEFAULT now()
reviewer_user_iduuidnullable, FK → User→ Cluster 1: User
reviewed_attimestamptznullable
statusenum(submitted, under_review, approved, partially_approved, denied)NOT NULL, DEFAULT 'submitted'

TransferCreditItem

AttributeTypeConstraintsNotes
iduuidPK
request_iduuidNOT NULL, FK → TransferCreditRequest
external_course_codetextNOT NULLCourse code at the external institution
external_course_titletextNOT NULL
external_creditsnumeric(4,2)NOT NULL, CHECK > 0
external_gradetextnullable
equivalent_course_iduuidnullable, FK → Course→ Cluster 2: Course
credits_grantednumeric(4,2)nullableCredits accepted; null until a decision is made
decisionenum(accepted, rejected, pending)NOT NULL, DEFAULT 'pending'
decision_notestextnullable

TransferCreditAppeal

AttributeTypeConstraintsNotes
iduuidPK
item_iduuidNOT NULL, FK → TransferCreditItem
student_iduuidNOT NULL, FK → Student→ Cluster 4: Student
reasontextNOT NULL
submitted_attimestamptzNOT NULL, DEFAULT now()
reviewer_user_iduuidnullable, FK → User→ Cluster 1: User
reviewed_attimestamptznullable
statusenum(submitted, under_review, approved, denied)NOT NULL, DEFAULT 'submitted'
outcome_notestextnullable

DocumentType

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULL, UNIQUEe.g., Official Transcript
codetextNOT NULL, UNIQUEe.g., OFFICIAL_TRANSCRIPT
descriptiontextnullable
retention_yearsintegerNOT NULLMinimum years to retain per policy
requires_staff_reviewbooleanNOT NULL, DEFAULT falseWhether uploaded documents need staff review

Document

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidnullable, FK → Student→ Cluster 4: Student; null for institution-level docs
document_type_iduuidNOT NULL, FK → DocumentType
uploaded_byuuidNOT NULL, FK → User→ Cluster 1: User
file_nametextNOT NULLOriginal filename
file_sizeintegerNOT NULLBytes
mime_typetextNOT NULL
storage_keytextNOT NULL, UNIQUEObject-storage key; never exposed directly
statusenum(pending, under_review, accepted, rejected)NOT NULL, DEFAULT 'pending'
reviewed_byuuidnullable, FK → User→ Cluster 1: User
reviewed_attimestamptznullable
review_notestextnullable
expires_attimestamptznullable

GeneratedDocument

AttributeTypeConstraintsNotes
iduuidPK
student_iduuidnullable, FK → Student→ Cluster 4: Student
document_type_iduuidNOT NULL, FK → DocumentType
generated_byuuidNOT NULL, FK → User→ Cluster 1: User
template_versiontextNOT NULLVersion of the document template used
file_nametextNOT NULL
storage_keytextNOT NULL, UNIQUE
generated_attimestamptzNOT NULL, DEFAULT now()
expires_attimestamptznullable

Notification

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidNOT NULL, FK → User→ Cluster 1: User
channelenum(email, sms, in_app)NOT NULL
event_typetextNOT NULLe.g., enrollment.confirmed, waitlist.offered
subjecttextNOT NULL
bodytextNOT NULL
sent_attimestamptznullableNull if pending
read_attimestamptznullableIn-app channel only
statusenum(pending, sent, failed, read)NOT NULL, DEFAULT 'pending'

NotificationPreference

AttributeTypeConstraintsNotes
iduuidPK
user_iduuidNOT NULL, FK → User→ Cluster 1: User
event_typetextNOT NULLMatches Notification.event_type
channelenum(email, sms, in_app)NOT NULL
is_enabledbooleanNOT NULL, DEFAULT true

Unique constraint on (user_id, event_type, channel).


Announcement

AttributeTypeConstraintsNotes
iduuidPK
created_byuuidNOT NULL, FK → User→ Cluster 1: User
titletextNOT NULL
bodytextNOT NULL
audience_roleenum(all, student, instructor, advisor, academic_staff, registrar, administrator)NOT NULL, DEFAULT 'all'
target_program_iduuidnullable, FK → Program→ Cluster 2: Program; null = all programs
publish_attimestamptzNOT NULL
expires_attimestamptznullable
statusenum(draft, published, archived)NOT NULL, DEFAULT 'draft'

SupportTicket

AttributeTypeConstraintsNotes
iduuidPK
submitted_byuuidNOT NULL, FK → User→ Cluster 1: User
assigned_touuidnullable, FK → User→ Cluster 1: User
subjecttextNOT NULL
categorytextNOT NULLe.g., billing, registration, technical
priorityenum(low, medium, high, urgent)NOT NULL, DEFAULT 'medium'
statusenum(open, in_progress, waiting_on_user, resolved, closed)NOT NULL, DEFAULT 'open'
resolved_attimestamptznullable

TicketMessage

AttributeTypeConstraintsNotes
iduuidPK
ticket_iduuidNOT NULL, FK → SupportTicket
author_user_iduuidNOT NULL, FK → User→ Cluster 1: User
bodytextNOT NULL
is_internalbooleanNOT NULL, DEFAULT falseInternal notes not visible to the submitter
sent_attimestamptzNOT NULL, DEFAULT now()

HelpArticle

AttributeTypeConstraintsNotes
iduuidPK
titletextNOT NULL
slugtextNOT NULL, UNIQUEURL-safe identifier
categorytextNOT NULLe.g., registration, billing
bodytextNOT NULLMarkdown content
author_user_iduuidNOT NULL, FK → User→ Cluster 1: User
published_attimestamptznullable
statusenum(draft, published, archived)NOT NULL, DEFAULT 'draft'

ScheduledReport

AttributeTypeConstraintsNotes
iduuidPK
nametextNOT NULL
report_typetextNOT NULLe.g., enrollment_summary, grade_distribution
created_byuuidNOT NULL, FK → User→ Cluster 1: User
recipient_emailstext[]NOT NULL, DEFAULT ''
schedule_crontextNOT NULLCron expression
last_run_attimestamptznullable
next_run_attimestamptznullable
formatenum(csv, pdf, xlsx)NOT NULL, DEFAULT 'csv'
parametersjsonbNOT NULL, DEFAULT ''Report-specific filter and option values
statusenum(active, paused)NOT NULL, DEFAULT 'active'

4. Key Design Decisions

Single-Table User with Joined-Table Inheritance for Student

All human principals share the User table and authenticate with a single credential. The role column determines permissions (RBAC enforced at the application layer). Student is a separate profile table whose id is a FK to User.id (joined-table inheritance pattern). This avoids multi-table polymorphism while keeping the student's extended profile separate from authentication concerns.

Grade on Enrollment; GradeCorrection as Audit Trail

Enrollment.grade holds the current authoritative grade. Any post-submission change creates a GradeCorrection record (an immutable audit entry), preserving the original grade and documenting who submitted and who approved the change. This satisfies FERPA audit requirements without destroying history.

Application-Managed Waitlist Position

Waitlist.position is an integer maintained by the application — not a DB sequence. The DB enforces UNIQUE (student_id, section_id) to prevent duplicate entries. When a student leaves the waitlist, the application re-numbers positions in a single transaction. This keeps logic simple while supporting configurable priority rules (e.g., reserved-seat priority).

Immutable DegreeAuditSnapshot JSON Blob

DegreeAuditSnapshot.audit_data stores the full audit result as jsonb at the moment of graduation clearance. This prevents retroactive curriculum changes from invalidating an approved graduation. The snapshot is written once and never mutated; is_graduation_clearance = true marks the canonical clearance snapshot.

Optimistic Concurrency for Section Enrollment

Section capacity enforcement (Section.capacity vs. enrolled_count) requires a concurrency-safe check during peak registration. The recommended approach is INSERT INTO Enrollment … RETURNING id combined with a DB CHECK constraint or trigger that validates enrolled_count < capacity on the Section row. An alternative is SELECT … FOR UPDATE on the Section row, which provides stronger guarantees at the cost of higher lock contention.

Calculated GPA and Credit Hours

Student.cumulative_gpa and Student.total_credit_hours_completed are marked [calculated]. The recommended implementation is a PostgreSQL materialized view refreshed on INSERT/UPDATE to Enrollment, or a trigger that recalculates on grade posting. These values must not be manually writeable columns to avoid inconsistency with the underlying Enrollment records.

Field-Level Diff in CourseChangeHistory

CourseChangeHistory stores one row per changed field (field_name, old_value, new_value as text). This is easy to implement, query, and test. An alternative is a single JSON-patch blob per change event, which is more compact but requires JSON functions for querying. The current row-per-field design is preferred for readability and compatibility with standard test automation tools.

Payment Gateway Token Storage

SavedPaymentMethod.provider_token stores only the opaque token returned by the payment gateway (e.g., Stripe, Braintree). Raw card numbers (PAN) are never stored in, transmitted through, or logged by the application tier. This design keeps the system out of PCI DSS scope for card data storage and reduces breach impact.