Skip to main content

Medication Service — Domain Model

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

1. Aggregates​

AggregateRoot entityKey invariants
MedicationListItemMedicationListItemOne active entry per (patient, medicationCode, source); status transitions follow FHIR MedicationRequest.status vocabulary
PrescriptionPrescriptionSigned prescriptions cannot modify dose/drug; discontinued is terminal; refillsDispensed ≤ refillsAuthorized
DispensingEventDispensingEventDispense requires active prescription; atomic inventory decrement; Schedule II requires counter-sign by distinct actor
StockItemStockItemquantity_on_hand ≥ 0; expired or recalled lots blocked from dispense
MedicationAdministrationMedicationAdministrationImmutable once recorded; corrections are new entries linked to prior
ReconciliationSessionReconciliationSessionOne open session per (patient, encounter, type); completion freezes diff decisions
AlertOverrideAlertOverrideReason ≥ 10 chars; drug-KB snapshot version required; actor distinct from system

2. Prescription State Machine​

3. Entities & Value Objects​

NameTypeNotes
MedicationVOCode (RxNorm/ATC/NDC/local), display, form, strength, isControlled, schedule
SigVODose amount + unit, route, frequency, duration, PRN indication, additional instructions
QuantityVOAmount + unit (UCUM)
DoseVOAmount + unit; includes min/max range for check
PrescriberSignatureVOActor ID, signed_at, credential ref, signing device
CounterSignVOActor ID (≠ dispenser), timestamp, reason
LotNumberVOManufacturer lot identifier
ExpiryDateVOISO date; validation at receipt + dispense
ReferenceRangeVOFor dose sanity, context: pediatric/adult/renal
KbSnapshotVODrug KB version + hash at alert evaluation
RouteCodeVOCoded route (oral, IV, IM, SC, topical, ophthalmic, etc.)

4. Aggregate Relationships​

5. Domain Events​

EventEmitted whenFHIR resource
medication.prescription.drafted.v1Prescriber starts a draftMedicationRequest.status=draft
medication.prescription.signed.v1Sign action, passes safety checks or overrideMedicationRequest.status=active
medication.prescription.discontinued.v1Discontinue with reasonMedicationRequest.status=stopped
medication.prescription.held.v1HoldMedicationRequest.status=on-hold
medication.prescription.resumed.v1ResumeMedicationRequest.status=active
medication.alert.overridden.v1Prescriber overrides blocking alertDetectedIssue
medication.dispensing.completed.v1Pharmacist completes dispenseMedicationDispense
medication.dispensing.partial.v1Partial dispenseMedicationDispense.status=in-progress
medication.dispensing.returned.v1Return/undispenseMedicationDispense.status=cancelled
medication.administration.recorded.v1MAR eventMedicationAdministration
medication.reconciliation.completed.v1Reconciliation session completeMedicationStatement[]
medication.inventory.stock_received.v1GRNSupplyDelivery
medication.inventory.reorder_alert.v1Below reorder pointn/a
medication.inventory.expiry_alert.v1Within expiry horizonn/a
medication.inventory.recall_triggered.v1Lot marked recalledDetectedIssue
medication.controlled_substance.dispensed.v1Schedule II–V dispenseMedicationDispense + Provenance

6. Ubiquitous Language​

TermDefinition
SigStructured prescription instructions (dose/route/frequency/duration)
MARMedication Administration Record; list of MedicationAdministration events
DAWDispense As Written — brand substitution prohibited
Counter-signSecond-actor signature required for controlled-substance dispense
Accessioning (pharmacy)Inventory receipt registration (not same as LIS)
FormularyTenant-curated list of allowed medications with substitution rules
ReconciliationComparing "home meds" vs facility meds with explicit continue/discontinue/modify per line
KB snapshotVersion hash of drug knowledge base at alert evaluation time
Partial dispenseDispense of quantity < total authorized, with remaining balance
Emergency overridePharmacist override of blocking check (insufficient stock, expired lot) with captured reason

7. Invariants (enforced in domain)​

  1. Prescription may only be signed after safety checks pass or every blocking alert has an AlertOverride.
  2. Dispense cannot proceed against expired or recalled lot — even with prescription active.
  3. Schedule II dispense requires counterSign.actor ≠ dispenser.actor.
  4. Inventory atomic decrement: quantity_on_hand must not go negative.
  5. Reconciliation completion requires each list item explicitly actioned (continue / discontinue / modify).
  6. A discontinued prescription cannot be re-signed; a new prescription replaces it.
  7. Override reason must be ≥ 10 printable characters.

8. Open Questions​

  • Licensed drug KB vendor choice (First Databank vs Medi-Span vs local+WHO EML) — pending tenant policy.
  • Pediatric dose-range computations for weight-banded sigs: formulary-driven vs KB-driven.