04-clinicalwave: W2filled9 citations

Clinical Assessments — Structured Patient Evaluations

Audiences: doctor, nurse, clinical-buyer, developer

Clinical Assessments — Structured Patient Evaluations

Clinical assessments are completed instances of assessment form definitions — when a clinician assigns a PHQ-9 or GAF scale to a patient, the resulting filled form is a FHIR QuestionnaireResponse backed by a Prisma AssessmentFormInstance, progressing through a reviewed lifecycle.

Business Purpose

Assessment scales and structured evaluations (PHQ-9, GAF, ADL, pain scales, medication adherence questionnaires) are essential clinical tools. Without a digital instance lifecycle, scores are recorded in paper notes, are not queryable over time, and cannot feed AI models or trigger clinical alerts.

Dudoxx HMS models each completed evaluation as an AssessmentFormInstance with:

  • A clear status machine: DRAFT → IN_PROGRESS → SUBMITTED → APPROVED/REJECTED.
  • A FHIR QuestionnaireResponse resource created on submission — making assessment data interoperable.
  • A review workflow: clinicians can approve or reject submitted assessments with a reason.
  • Guest submission support: patients can complete assessments without logging in via time-limited public tokens.
  • Proactive form assignment: the system can push assessment forms to patients at defined intervals or clinical triggers.

Audiences

  • Investor: Scored assessments (PHQ-9, GAF) create a longitudinal patient mental health record that feeds AI trend analysis and enables value-based care reporting.
  • Clinical buyer (doctor/nurse/receptionist): Clinicians assign assessments to patients and review submissions. The approval workflow ensures clinicians sign off on patient-reported outcomes before they enter the record.
  • Developer/partner: Create instance: POST /api/v1/assessment-form-instances. Submit: PATCH /api/v1/assessment-form-instances/:id/submit. Review: PATCH /api/v1/assessment-form-instances/:id/approve or /reject. Guest submit: POST /api/v1/public-forms/:token/submit.
  • Internal (ops/support): Instance data in Prisma ddx_api_main (AssessmentFormInstance, AssessmentFormInstanceAttachment). FHIR QuestionnaireResponse created on submission. Proactive forms triggered via ProactiveFormsController (scheduled or event-driven).

Architecture

Controller (route)ServiceResponsibility
AssessmentFormInstancesController (/api/v1/assessment-form-instances)AssessmentFormInstancesServiceorchestrator
FormInstanceQueryServicelist/filter instances by patient, form, status
FormInstanceSubmitServiceSUBMITTED transition + FHIR QuestionnaireResponse write
FormInstanceReviewServiceAPPROVED/REJECTED transitions with reason
FormInstancePatientServicepatient-scoped access (no cross-patient leakage)
QuestionnaireResponseDescriptorFHIR resource sync
GuestSubmissionsController (/api/v1/public-forms/:token)PublicFormAccessServicetoken validation + guest submission routing
ProactiveFormsController (/api/v1/proactive-forms)(assignment push logic)assigns forms to patients based on triggers/schedule

The QuestionnaireResponseDescriptor mirrors the pattern from encounter.descriptor.ts (NF2 facade) — on submission, it creates a FHIR QuestionnaireResponse resource in HAPI FHIR and upserts a fhir_resource_link row in Prisma.

Tech Stack & Choices

LayerTechnologyNotes
Instance storagePrisma ddx_api_main (AssessmentFormInstance)Status machine, timestamps, assignedBy, patient link
FHIR syncHAPI FHIR QuestionnaireResponse via descriptorCreated on SUBMITTED transition
Instance attachmentsMinIO via AssessmentFormInstanceAttachmentsServiceAttached files to assessment submissions
Guest accessTime-limited public tokens (PublicFormAccessService)No JWT required; token scope: single form
Status machineFormInstanceStatus enumDRAFT → IN_PROGRESS → SUBMITTED → APPROVED/REJECTED/CANCELLED
Auth@ProtectedRead/Write/Manage('assessment-forms')Role-permission model; PATIENT role can access own instances via patient service
Zodzod/v4 for DTO validationNestJS canonical Zod import

Data Flow

Clinician assigns and submits an assessment

Business outcome: Clinician assigns a PHQ-9 to a patient; patient fills it in (or clinician fills on behalf); clinician reviews and approves — score enters the patient's structured record.

Technical mechanism:

  1. POST /api/v1/assessment-form-instances with { formDefinitionId, patientId, assignedBy } → creates instance in DRAFT status.
  2. Clinician/patient fills field values → PATCH .../start transitions to IN_PROGRESS.
  3. PATCH .../submit with field answers → FormInstanceSubmitService.submit(): a. Validates required fields are answered. b. Transitions status to SUBMITTED. c. QuestionnaireResponseDescriptor creates FHIR QuestionnaireResponse with answer items mapped from field values. d. fhir_resource_link Prisma row upserted for cross-store audit.
  4. PATCH .../approve with { reviewerId, reason }FormInstanceReviewService.approve() → status APPROVED.

Patient completes a guest form

POST /api/v1/public-forms/:token/submitGuestSubmissionsController validates token (time-limited, form-scoped), routes submission to FormInstanceSubmitService. No JWT required — guest route is @RbacExempt().

Implicated Code

  • ddx-api/src/clinical-forms/assessment-forms/assessment-form-instances.controller.ts:1AssessmentFormInstancesController — instance lifecycle (create, start, submit, approve, reject)
  • ddx-api/src/clinical-forms/assessment-forms/assessment-form-instances.service.ts:1AssessmentFormInstancesService — orchestrates 4 sub-services
  • ddx-api/src/clinical-forms/assessment-forms/form-instance-submit.service.ts:1FormInstanceSubmitService — SUBMITTED transition + FHIR QuestionnaireResponse write
  • ddx-api/src/clinical-forms/assessment-forms/form-instance-review.service.ts:1FormInstanceReviewService — APPROVED/REJECTED transitions with reason text
  • ddx-api/src/clinical-forms/assessment-forms/form-instance-query.service.ts:1FormInstanceQueryService — paginated queries by patient, form definition, status
  • ddx-api/src/clinical-forms/assessment-forms/form-instance-patient.service.ts:1FormInstancePatientService — patient-scoped instance access (no cross-patient data)
  • ddx-api/src/clinical-forms/assessment-forms/guest-submissions.controller.ts:1GuestSubmissionsController — unauthenticated form submission via public token
  • ddx-api/src/clinical-forms/assessment-forms/proactive-forms.controller.ts:1ProactiveFormsController — push form assignments to patients
  • ddx-api/src/clinical-forms/assessment-forms/questionnaire-response.descriptor.ts:1 — FHIR QuestionnaireResponse resource descriptor

Operational Notes

  • FHIR write on submission: Every SUBMITTED transition triggers a FHIR QuestionnaireResponse write. If HAPI FHIR is unavailable, submission fails. Ensure HAPI FHIR health before high-volume assessment campaigns.
  • Guest token security: Public form tokens are time-limited but not single-use by default. Evaluate rate-limiting and expiry settings before exposing patient-facing assessment URLs. Token management: PublicFormAccessService + AssessmentFormsController public token endpoints.
  • Status is terminal after APPROVED/REJECTED: Once an instance reaches APPROVED or REJECTED, it cannot be re-submitted. If a patient needs to redo an assessment, create a new instance.
  • Patient service isolation: FormInstancePatientService enforces patient-scoped access — a patient cannot read another patient's assessment instances even if they know the instance ID. This is enforced at service layer, not only by RBAC.
  • Attachment MIME types: AssessmentFormInstanceAttachmentsService stores files in MinIO. Validate MIME type allowlist before enabling attachment upload on public-facing forms.