ADR-013: Doctor Service and Separate Organization Affiliations
Status
Accepted. Supersedes ADR-004 and the doctor-profile storage references in ADR-005.
Decision
Introduce a doctor module owning professional doctor profile records. Identity continues to own credentials and authentication. Doctor registration orchestrates identity account creation and doctor profile creation via curated service contracts.
Organization remains authoritative for ordinary memberships and separately owned doctor affiliations, invitations, and join requests. An organization admin is an organization member and is independent from the platform admin service. Assigning an ordinary doctor member role does not create a professional profile or affiliation.
A doctor may affiliate with several organizations. The planned self-service path for independently registered doctors uses an organization-admin-approved join request. Invited registration for a new identity account creates affiliation directly when a matching-email, single-use invitation is redeemed. Existing doctors use join requests for self-service onboarding.
Organization-scoped doctor views expose only affiliated doctors, with profile data provided by the doctor service. Cross-service operations use curated public interfaces, never another service's repositories, internal domain entities, or whole module. Events and email delivery will occur only after authoritative changes succeed.
Initial implementation boundary
The original API layer consisted of Huma/OpenAPI contracts and one TODO use case per operation returning 501. Authentication and permissions were documented, not enforced. That pass introduced no persistence migrations, verification workflow, prescription changes, subscription implementation, or notification delivery. The subsequent implementation below extends that boundary; prescription eligibility remains deferred.
Subsequent profile persistence and response fields
Doctor now persists professional profiles and implements registration, login, authentication verification, and current-profile retrieval. Identity remains the source of current account names and credentials. The professional profile ID is distinct from the Identity account ID; license_number is the registration credential, with no separate registration-number alias.
Profiles expose one speciality catalog object, an ordered specialization catalog array, an optional qualification summary, and multiple chambers. Each specialization references exactly one parent speciality, and database constraints require every selected child to belong to the doctor's selected parent. Doctor owns the qualification, speciality, and specialization catalogs; there is no separate subspecialties section.
Registration selects catalog IDs. Migrations preserve child IDs and original expertise text, automatically map unambiguous profiles, and require explicit reconciliation for multiple parents or unmatched legacy text. Existing historical verification and prescription summaries retain their snapshot meaning. General existing-account profile creation and PATCH remain 501 contracts. See the Doctor guide and catalog contracts and migration procedure.
Subsequent Admin affiliation management
Organization persists doctor affiliations separately from ordinary memberships. Platform administrators can assign or reactivate affiliations through a bulk POST and list, inspect, or change an existing affiliation under /api/v1/admin/organizations/{organizationId}/doctors. These operations require an active Identity account, active Admin membership, and explicit operation permissions. Reads and status changes additionally require admin.organization.read; PATCH requires admin.doctor.affiliation.update. Existing administrators need an explicit grant after the permission migration.
Admin and the Organization portal reuse an Organization-owned public reader, with each calling use case enforcing its own permissions. Detail reads use the exact organization/profile pair; all reads include current Doctor, Identity, and verification data. Admin may inspect suspended or deleted organizations. Listing includes active and inactive affiliations unless filtered; missing required enrichment fails closed.
Organization owns the transaction for assignment and status changes, including the corresponding audit record. PATCH requires an existing relationship. Deactivation retains assignment provenance and joined_at; reactivation refreshes assignment provenance while preserving joined_at. Repeated valid states produce no additional change event. Activation requires an active organization and doctor account, checked under locks that serialize with account suspension and organization lifecycle changes. Deactivation remains available for inactive accounts and organizations. Other affiliations, ordinary memberships, and global account status are preserved.
Verification and mock subscription data remain display-only for assignment; subscription capacity enforcement and portal detail/status operations remain deferred. See Doctor administration.
Subsequent Prescription Contracts
ADR-015 adds the separate Patient service and prescription HTTP contracts. The exclusions above describe this doctor-service decision's original scope. Prescription contracts now document active affiliation and author-only mutations, but still return 501 without eligibility, verification or subscription enforcement.
Consequences
Professional data has a dedicated owner, organization membership stays authoritative, and remaining frontend onboarding can be built against explicit API schemas. Implemented registration commits Identity provisioning, the professional profile, and optional invitation redemption/affiliation atomically. Future join-request acceptance must likewise commit request state and affiliation together.
Related decisions
- ADR-004: Doctor as Role (superseded)
- ADR-005: Doctor Verification in Admin Service (profile storage superseded)
- ADR-011: One Use Case per File
- ADR-012: Explicit Public Service Contracts
- ADR-017: Scoped Authorization Service
- ADR-018: Shared Audit Service