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.