Goals — Data Model
Schema: libs/db/src/lib/schema/goals.ts.
goals
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
membership_id | uuid NOT NULL | FK memberships(id) |
organization_id | uuid NOT NULL | |
type | goal_type NOT NULL | body_metric or exercise_pr |
metric_type | varchar(50) | Set when type=body_metric |
exercise_id | uuid | FK exercises(id). Set when type=exercise_pr |
direction | goal_direction NOT NULL DEFAULT 'increase' | FIT-119 — without this, decrease goals auto-achieve |
target_value_numeric | numeric(14,4) NOT NULL | Comparison runs without parseFloat |
start_value_numeric | numeric(14,4) | Baseline; nullable for legacy pre-FIT-119 rows |
unit | varchar(50) NOT NULL | kg, lb, %, cm, seconds, … |
deadline | date | Optional |
status | goal_status NOT NULL DEFAULT 'active' | active, achieved, cancelled |
achieved_at | timestamptz | Set when status flips to achieved |
created_at / updated_at / deleted_at | timestamptz | Soft-delete |
Constraints:
goals_type_target_chk—(type='body_metric' AND metric_type IS NOT NULL AND exercise_id IS NULL) OR (type='exercise_pr' AND exercise_id IS NOT NULL AND metric_type IS NULL).
Indexes:
goals_membership_status_idxpartial on(membership_id, status)WHEREdeleted_at IS NULL.goals_org_idxonorganization_id.goals_exercise_idxpartial on(exercise_id)WHERE NOT NULL ANDdeleted_at IS NULL.
Multi-org isolation
- Both
membership_idandorganization_idcarried. - A user with memberships in two orgs has separate goal sets per org.
PII handling
- Goal data is sensitive-ish (weight loss targets) but less so than body_metrics; same plaintext / no field-level encryption stance.
Soft / hard delete
- Soft via
deleted_at. No hard-delete path.