Communication Service — Application Logic
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template
1. Use cases
1.1 Commands
| Command | Aggregate | FR |
|---|---|---|
CreateThread | MessageThread | FR-COMMS-MSG-001 |
SendMessage | MessageThread → Message | FR-COMMS-MSG-003 |
MarkMessagesRead | MessageThread (read receipts) | FR-COMMS-MSG-004 |
ArchiveThread | MessageThread | FR-COMMS-MSG-005 |
EscalateThread | MessageThread | FR-COMMS-MSG-006 |
AddAttachment | Message | FR-COMMS-MSG-008 |
SubmitNotificationIntent | NotificationIntent | FR-COMMS-NOTIF-001 |
RecordDispatchOutcome | DispatchRecord | FR-COMMS-NOTIF-004 |
CreateVirtualSession | VirtualSession | FR-COMMS-VC-001 |
IssueJoinToken | VirtualSession | FR-COMMS-VC-002 |
AdmitParticipant | VirtualSession | FR-COMMS-VC-003 |
EndVirtualSession | VirtualSession | FR-COMMS-VC-004 |
CancelVirtualSession | VirtualSession | FR-COMMS-VC-005 |
SpawnFallbackThread | VirtualSession → MessageThread | FR-COMMS-VC-023 |
1.2 Queries
| Query | Returns |
|---|---|
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)
| Port | Purpose |
|---|---|
MessageThreadRepository | CRUD + search for threads |
MessageRepository | Append-only message log per thread |
NotificationIntentRepository | Idempotent upsert by (tenantId, correlationId, channel) |
DispatchLogRepository | Append-only dispatch outcomes |
VirtualSessionRepository | State-machine-aware persistence |
SmsProvider | send(SmsDispatchRequest): DispatchOutcome |
PushNotificationProvider | send(PushDispatchRequest): DispatchOutcome |
EmailProvider | send(EmailDispatchRequest): DispatchOutcome |
VirtualMeetingProvider | createRoom / issueToken / end (Jitsi impl) |
FhirCommunicationClient | Write Communication through interop-service |
TemplateRenderer | Render template keys with opaque variables |
ClockPort | Testable time source |
OutboxPort | Transactional outbox write |
4. Saga / outbox patterns
- Outbox relay: every domain transaction that mutates state writes to the
outboxtable in the same DB transaction; a relay publishes to NATS with ordered ACK. - Notification dispatch saga:
NotificationIntentpersisted → outbox emits.queued→ worker claims → adapter call →DispatchRecordpersisted → outbox emits.dispatched|.failed→ async DLR webhook upserts.delivered|.undeliverable. - Virtual-session fallback saga: failure detection (heartbeat loss or adapter error) →
spawnFallbackThreaduse case → message thread + notification intent for all participants → both emit events for audit.
5. Error handling
| Situation | Behavior |
|---|---|
| Unlicensed tenant | 403 MODULE_NOT_LICENSED |
| Non-participant accessing thread | 403 FORBIDDEN + audit |
| Attachment rejected (virus / size) | 422 ATTACHMENT_REJECTED with reason; quarantine event |
| Provider adapter down | Return 202 queued; retry with exponential backoff; failover to secondary provider if configured |
| Idempotency-key collision | Return prior result with 202 Accepted |
| Virtual session in terminal state | 409 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.