Virtual Care Service — Domain Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
1. Aggregates
1.1 VirtualSession
The central aggregate. Represents a single telehealth session from creation through termination.
Invariants:
VirtualSessionrow is created only after the video room is successfully provisioned (no session row on backend failure).patientIdis always required; a session cannot exist without a patient.recordingEnabledcan only be set totrueif patient recording consent is on file.versionis used for optimistic locking; every state transition increments version.- An
endedsession cannot be reactivated. - Join tokens expire after
sessionGraceMinutesBeforeconfigured value (default 15 min before scheduled start).
State machine:
| State | Meaning |
|---|---|
scheduled | Session created; no participants in room |
waiting | At least one participant joined; waiting for provider to admit |
active | Provider has admitted patient; session in progress |
ended | Session ended normally; Encounter creation triggered |
cancelled | Cancelled before becoming active |
failed | Video backend lost; grace reconnect expired |
1.2 SessionParticipant
Entity within VirtualSession aggregate. One per participant.
Invariants:
rolemust be one of:provider | patient | interpreter | observer- At most one
patientparticipant per session at a time. - Provider must be present before patient can be admitted from waiting room.
- A
removedparticipant cannot re-join without a new token.
Participant states: invited → waiting → admitted → active → disconnected → removed
1.3 TenantVirtualCareConfig
Per-tenant configuration aggregate. One record per tenant.
Invariants:
jitsiJwtSecretis never returned in GET responses (redacted as****).maxParticipantsmust be between 2 and the platform hard limit (default 20).jitsiServerUrlmust be HTTPS.brandingLogoUrlandbrandingPrimaryColorare validated (HTTPS allowlist, CSS hex color).
1.4 AsyncVisit (store-and-forward)
Represents an asynchronous telehealth encounter where the provider reviews clinical content and responds, rather than conducting a live video session.
Invariants:
- Content is authored offline and submitted when connectivity restores.
- Once submitted, content is immutable (new version required for amendments).
- Provider response triggers Encounter creation in patient-chart-service.
States: draft → submitted → under_review → responded → closed
2. Value Objects
| Value Object | Fields | Notes |
|---|---|---|
JoinToken | token: string, expiresAt: Date, participantId: string | HMAC-signed; short-lived |
VideoBackend | jitsi | mediasoup | zoom | webex | teams | Branded enum |
SessionStatus | state machine states above | Branded string |
ParticipantRole | provider | patient | interpreter | observer | Branded string |
BandwidthProfile | video | audio_only | async_text | Fallback tier |
ConsentRecord | type: telehealth|recording, capturedAt: Date, version: string | Immutable on capture |
3. Domain Events
| Event | Aggregate | Trigger |
|---|---|---|
virtual_care.session.created.v1 | VirtualSession | Session + room provisioned |
virtual_care.session.started.v1 | VirtualSession | First provider joins; session → active |
virtual_care.session.participant.joined.v1 | SessionParticipant | Participant validates join token |
virtual_care.session.participant.admitted.v1 | SessionParticipant | Provider admits patient from waiting room |
virtual_care.session.participant.removed.v1 | SessionParticipant | Provider removes participant |
virtual_care.session.ended.v1 | VirtualSession | Session ends normally |
virtual_care.session.cancelled.v1 | VirtualSession | Session cancelled before active |
virtual_care.session.failed.v1 | VirtualSession | Grace reconnect expired |
virtual_care.session.fallback.initiated.v1 | VirtualSession | Async messaging fallback triggered |
virtual_care.config.updated.v1 | TenantVirtualCareConfig | Tenant config changed |
virtual_care.recording.ingested.v1 | VirtualSession | Jibri recording reference stored |
virtual_care.billing.session_chargeable.v1 | VirtualSession | Session ended with Encounter ID for billing |
virtual_care.async_visit.submitted.v1 | AsyncVisit | Store-and-forward content submitted |
4. Ubiquitous Language
| Term | Definition |
|---|---|
| Virtual session | A telehealth encounter record representing one video or async consultation |
| Waiting room | The pre-admission state where patients wait for the provider to admit them |
| Join token | A short-lived HMAC-signed credential allowing a specific participant to enter a session |
| Admit | Provider action that moves a patient from waiting room to active session |
| Bandwidth fallback | Automatic degradation: video → audio-only → async text when connectivity is insufficient |
| Store-and-forward | Async telehealth: patient submits text/images offline; provider reviews later |
| Telehealth consent | Patient's explicit consent to participate in a telehealth session (required before join) |
| Recording consent | Patient's separate explicit consent for the session to be recorded |
| FHIR Encounter (VR) | The FHIR resource created when a virtual session ends; class=VR (virtual) |
| Jitsi Meet | Self-hosted open-source WebRTC video conferencing platform |
| Jibri | Jitsi Broadcasting Infrastructure — handles session recording |
| Video backend | The underlying video conferencing engine (Jitsi, Mediasoup, Zoom, etc.) |
| Grace reconnect | A configurable window (default 60s) allowing a dropped participant to rejoin before the session transitions to failed |