Virtual Care Service — Application Logic
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
1. Commands (Write Use Cases)
| Command | Handler | Description |
|---|---|---|
CreateVirtualSessionCommand | CreateVirtualSessionUseCase | Validate consent + license; provision Jitsi room; persist session row |
EndSessionCommand | EndSessionUseCase | FSM end; trigger Encounter creation; emit billing event |
CancelSessionCommand | CancelSessionUseCase | FSM cancel with reason; notify participants |
AdmitParticipantCommand | AdmitParticipantUseCase | Move participant from waiting → admitted; generate video join URL |
RemoveParticipantCommand | RemoveParticipantUseCase | Remove participant from active session |
InitiateFallbackCommand | InitiateFallbackUseCase | Trigger async messaging fallback; emit fallback event |
UpdateTenantConfigCommand | UpdateTenantConfigUseCase | Validate + encrypt credentials; persist; emit config.updated |
SubmitAsyncVisitCommand | SubmitAsyncVisitUseCase | Validate offline content; persist; notify provider |
AcceptAiSummaryCommand | AcceptAiSummaryUseCase | HITL acceptance gate; push accepted summary to patient-chart-service |
2. Queries (Read Use Cases)
| Query | Handler | Description |
|---|---|---|
GetSessionListQuery | GetSessionListUseCase | Paginated list with status/patient/date filters |
GetSessionDetailQuery | GetSessionDetailUseCase | Full session with participants |
GetJoinTokenQuery | GetJoinTokenUseCase | Issue/refresh join token for participant |
ValidateJoinTokenQuery | ValidateJoinTokenUseCase | Token-protected; returns destination (waiting_room or direct_join) |
GetParticipantListQuery | GetParticipantListUseCase | All participants with status |
GetTenantConfigQuery | GetTenantConfigUseCase | Config with credentials redacted |
3. Ports (Interfaces)
| Port | Type | Implementation |
|---|---|---|
VirtualSessionRepository | Repository | postgres-virtual-session.adapter.ts |
SessionParticipantRepository | Repository | postgres-session-participant.adapter.ts |
TenantConfigRepository | Repository | postgres-tenant-config.adapter.ts |
VideoProviderPort | Outbound | jitsi-meet.adapter.ts (normative) |
VideoProviderHealthPort | Outbound | jitsi-health-check.adapter.ts |
FhirGatewayPort | Outbound | fhir-gateway.adapter.ts (Encounter creation) |
EventPublisherPort | Outbound | nats-event-publisher.adapter.ts (outbox relay) |
AuditPort | Outbound | nats-audit.adapter.ts |
ConsentCheckPort | Outbound | consent-check.adapter.ts |
JoinTokenSignerPort | Outbound | hmac-join-token.adapter.ts |
SecretStorePort | Outbound | kms-secret.adapter.ts (Jitsi JWT secrets) |
AiGatewayPort | Outbound | ai-gateway.adapter.ts (STT + summary) |
OfflineQueuePort | Outbound | async-visit-queue.adapter.ts |
4. Orchestration Flows
4.1 Create Session Flow
4.2 Session End → Encounter Creation Flow
4.3 Bandwidth Fallback Flow
4.4 Appointment Auto-Create Flow (Event-driven)
5. Saga / Outbox Patterns
| Pattern | Where used |
|---|---|
| Outbox relay | All domain events via virtual_care_outbox table; relay after commit |
| Inbox deduplication | Consumed scheduling events deduped by CloudEvent id in virtual_care_inbox |
| Optimistic lock | Every session mutation checks version; conflict returns 409 |
| FHIR Encounter saga | If Encounter creation fails post-session-end, session row is marked ended but encounterId null; retry job reconciles within 5 min |
6. Error Handling
| Error | HTTP | Code | Action |
|---|---|---|---|
| Video backend unhealthy | 503 | VIDEO_PROVIDER_UNAVAILABLE | No session row created; return diagnostic |
| Consent not on file | 403 | CONSENT_REQUIRED | Block creation; log attempt |
| Module not licensed | 403 | MODULE_NOT_LICENSED | Return tenant licensing page hint |
| Invalid FSM transition | 409 | INVALID_STATUS_TRANSITION | Return current status |
| Optimistic lock conflict | 409 | OPTIMISTIC_LOCK_CONFLICT | Return current version |
| Participant not in waiting room | 409 | PARTICIPANT_NOT_IN_WAITING_ROOM | Cannot admit |
| Join token expired | 401 | TOKEN_EXPIRED | Caller must refresh token |
| Max participants exceeded | 422 | MAX_PARTICIPANTS_EXCEEDED | Tenant config limit |
| Provider quota exceeded | 503 | PROVIDER_QUOTA_EXCEEDED | Try secondary backend if configured |