Skip to main content

Communication Service — Application Logic

Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template

1. Use cases

1.1 Commands

CommandAggregateFR
CreateThreadMessageThreadFR-COMMS-MSG-001
SendMessageMessageThread → MessageFR-COMMS-MSG-003
MarkMessagesReadMessageThread (read receipts)FR-COMMS-MSG-004
ArchiveThreadMessageThreadFR-COMMS-MSG-005
EscalateThreadMessageThreadFR-COMMS-MSG-006
AddAttachmentMessageFR-COMMS-MSG-008
SubmitNotificationIntentNotificationIntentFR-COMMS-NOTIF-001
RecordDispatchOutcomeDispatchRecordFR-COMMS-NOTIF-004
CreateVirtualSessionVirtualSessionFR-COMMS-VC-001
IssueJoinTokenVirtualSessionFR-COMMS-VC-002
AdmitParticipantVirtualSessionFR-COMMS-VC-003
EndVirtualSessionVirtualSessionFR-COMMS-VC-004
CancelVirtualSessionVirtualSessionFR-COMMS-VC-005
SpawnFallbackThreadVirtualSession → MessageThreadFR-COMMS-VC-023

1.2 Queries

QueryReturns
ListThreads(filter)ThreadSummary[] filtered by patientId, unreadOnly, urgent, assignedToMe
GetThread(threadId)Thread detail with participants
ListMessages(threadId, cursor)Paginated messages
GetVirtualSession(sessionId)Session detail (identity-bound for PATIENT role)
ListDispatchStatus(correlationId)Outcome per channel for a correlation

2. Orchestration flows

2.1 Send secure message

2.2 Virtual session from scheduling

2.3 Fallback continuity

3. Ports (application interfaces)

PortPurpose
MessageThreadRepositoryCRUD + search for threads
MessageRepositoryAppend-only message log per thread
NotificationIntentRepositoryIdempotent upsert by (tenantId, correlationId, channel)
DispatchLogRepositoryAppend-only dispatch outcomes
VirtualSessionRepositoryState-machine-aware persistence
SmsProvidersend(SmsDispatchRequest): DispatchOutcome
PushNotificationProvidersend(PushDispatchRequest): DispatchOutcome
EmailProvidersend(EmailDispatchRequest): DispatchOutcome
VirtualMeetingProvidercreateRoom / issueToken / end (Jitsi impl)
FhirCommunicationClientWrite Communication through interop-service
TemplateRendererRender template keys with opaque variables
ClockPortTestable time source
OutboxPortTransactional outbox write

4. Saga / outbox patterns

  • Outbox relay: every domain transaction that mutates state writes to the outbox table in the same DB transaction; a relay publishes to NATS with ordered ACK.
  • Notification dispatch saga: NotificationIntent persisted → outbox emits .queued → worker claims → adapter call → DispatchRecord persisted → outbox emits .dispatched|.failed → async DLR webhook upserts .delivered|.undeliverable.
  • Virtual-session fallback saga: failure detection (heartbeat loss or adapter error) → spawnFallbackThread use case → message thread + notification intent for all participants → both emit events for audit.

5. Error handling

SituationBehavior
Unlicensed tenant403 MODULE_NOT_LICENSED
Non-participant accessing thread403 FORBIDDEN + audit
Attachment rejected (virus / size)422 ATTACHMENT_REJECTED with reason; quarantine event
Provider adapter downReturn 202 queued; retry with exponential backoff; failover to secondary provider if configured
Idempotency-key collisionReturn prior result with 202 Accepted
Virtual session in terminal state409 INVALID_STATE_TRANSITION

6. Idempotency

All mutation endpoints accept Idempotency-Key (UUID) and, optionally, X-Client-Mutation-Id. Server stores (tenantId, idempotencyKey) → response body for 24 hours. Offline-queued mobile requests use X-Client-Mutation-Id with the same semantics.