Provider Directory Service — Application Logic
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: DOMAIN_MODEL · API_CONTRACTS · EVENT_SCHEMAS
1. Use Cases
1.1 Commands
| Command | Aggregate | Notes |
|---|---|---|
CreatePractitioner | Practitioner | Validate identifier uniqueness per authority |
UpdatePractitioner | Practitioner | Optimistic lock |
DeactivatePractitioner | Practitioner | Ends all roles; blocks new selections |
SuspendPractitioner | Practitioner | Temporary; blocks selections but preserves data |
AddCredential | Practitioner (Credential child) | Auto-flags privileges |
UpdateCredential | Credential | Expiry extension |
RevokeCredential | Credential | Triggers privilege revocation cascade |
AssignPractitionerRole | Role | Validates required credentials (BR-PROV-001) |
EndPractitionerRole | Role | Sets period.end |
UpsertHealthcareService | HealthcareService | |
UpsertServiceEndpoint | Endpoint | |
RunEndpointHealthcheck | Endpoint | Scheduled; updates health status |
LinkPractitionerToUser | Practitioner | Bind userId from identity-service |
1.2 Queries
| Query | Returns |
|---|---|
SearchPractitioners | Fuzzy name/identifier/specialty; multi-script |
GetPractitioner(id) | Full profile |
ListPractitionerRoles(practitionerId) | All roles (active + historical) |
ListRolesByNode(nodeId) | Roles scoped to node |
ListEndpoints(orgNodeId?, type?) | Endpoints catalog |
CanPractitionerPerform(practitionerId, privilegeKey) | Boolean (hot path for order/laboratory/radiology services) |
ListExpiringCredentials(daysAhead) | For nightly expiry job |
2. Ports
| Port | Purpose |
|---|---|
PractitionerRepository | CRUD |
PractitionerRoleRepository | CRUD |
HealthcareServiceRepository | CRUD |
ServiceEndpointRepository | CRUD |
PractitionerSearchIndex | OpenSearch adapter |
TerminologyClient | Validate specialty codes |
AccessPolicyClient | POST /internal/access/evaluate |
LicensingClient | Module gate |
EventPublisher | Outbox-backed |
FhirProjector | Practitioner / Role / HealthcareService / Endpoint |
CommunicationClient | Credential expiry notifications |
FacilityClient | Node existence check (on role creation) |
EndpointHealthChecker | HTTP/TLS probe |
3. Orchestration
3.1 Role assignment with privilege gating (BR-PROV-001)
3.2 Credential expiry cascade
4. Saga / Outbox
- Transactional outbox for all events.
- Inbox dedup on
identity.user.registered.v1andfacility.provider_membership.*.
5. Error Handling
| Error | HTTP | Code |
|---|---|---|
| Duplicate identifier | 409 | IDENTIFIER_CONFLICT |
| Required credential missing | 422 | CREDENTIAL_REQUIRED |
| Provider deactivated | 409 | PRACTITIONER_DEACTIVATED |
| Specialty code not found | 422 | SPECIALTY_UNKNOWN |
| Node not found | 404 | HIERARCHY_NODE_NOT_FOUND |
| Access denied | 403 | ACCESS_DENIED |
| Optimistic lock | 409 | VERSION_MISMATCH |
6. Policies
| Policy | Trigger | Reaction |
|---|---|---|
OnUserRegisteredPolicy | identity.user.registered.v1 | Optional JIT shadow practitioner in pending_profile state if registrationSource=clinician_sso |
OnNodeMembershipAssignedPolicy | facility.provider_membership.assigned.v1 | Update cached provider scope summary |
OnGdprRequestPolicy | gdpr.subject_request.received.v1 | Anonymize practitioner record if no clinical history dependency |
7. Search
OpenSearch index practitioners_{tenant}_{version} with:
- N-gram analyser for Latin + Arabic scripts.
- ICU transliteration to support "Ahmad" ↔ "احمد" matching.
- Fields: names (all variants), identifiers, specialties, active status, node-scoped role summary.
- Rebuild: daily full; incremental on events.