Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesClass TypesClass Types — Data Model

Class Types — Data Model

Table: class_types in libs/db/src/lib/schema/scheduling.ts.

Columns

ColumnTypeNotes
iduuid PKdefaultRandom()
program_iduuid NOT NULLFK → programs.id. Org boundary — programs.organization_id is the org.
namevarchar(255) NOT NULLDisplay name.
descriptiontextOptional long-form.
default_duration_minintUsed to suggest endsAt in the session creator.
default_capacityintDefault capacity for new sessions (overridable).
default_location_iduuidFK → locations.id ON DELETE SET NULL. Default space for new sessions (ADR-0024 D4). Applied on session create when locationId is omitted and the space is active. Nullable. Added by migration 0167_spaces.
colorvarchar(7)Hex; renders calendar pill.
has_workoutbool NOT NULL default trueWhen false, schedule UX suppresses workout-attachment affordances (“Open Gym” pattern).
is_activebool NOT NULL default trueSoft-deactivate flag.
created_at / updated_attimestamptz
deleted_attimestamptzDefined on the column but never written by the service — remove sets is_active=false instead. TODO: verify whether any consumer reads deleted_at.

Indexes / constraints

No additional indexes declared in the schema file (only the PK). No UNIQUE constraints (multiple class types may share a name within an org). FK class_types_default_location_id_locations_id_fk (ON DELETE SET NULL).

Relations

  • program — one-to-one (mandatory parent).
  • defaultLocation — optional, → locations.
  • classSessions — one-to-many.
  • dailyProgramming — one-to-many (daily_programming.class_type_id).

Lifecycle of a row

  1. Create — INSERT with is_active=true, has_workout=true default. Service validates the program belongs to the caller’s org.
  2. Update — partial PATCH; no field except program_id can change (program_id is intentionally omitted from UpdateClassTypeDto).
  3. Deactivate — UPDATE … SET is_active=false. Historical references remain valid.

No row is ever hard-deleted by the service.

Multi-org isolation pattern

class_types has no direct organization_id column. Org-scoping pivots through the parent programs.organization_id. Every service method:

  1. Calls MembershipsService.requireMembership(orgId, clerkId) (403 if not a member).
  2. Joins / fetches the parent program and verifies program.organizationId === orgId (404 if mismatch).

Soft-delete vs hard-delete

Soft only, via is_active. The deleted_at column exists but is unused — kept for parity with sibling tables. findAll filters deleted_at IS NULL defensively even though nothing writes it today.