Skip to main content

Orders Service — Data Model

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template

1. TypeScript Domain Interfaces

export type OrderType = 'medication' | 'laboratory' | 'radiology' | 'procedure' | 'referral' | 'nursing' | 'diet' | 'activity';
export type OrderStatus = 'draft' | 'active' | 'on-hold' | 'completed' | 'cancelled' | 'entered-in-error';
export type OrderPriority = 'routine' | 'urgent' | 'stat' | 'asap';

export interface Order {
id: string; // ord_ prefix
tenantId: string;
patientId: string;
encounterId: string;
orderType: OrderType;
orderCode: CodeableConcept;
status: OrderStatus;
priority: OrderPriority;
orderedBy: string; // userId
orderedAt: string; // ISO datetime
// Type-specific details (one of):
medicationDetail?: MedicationDetail;
labDetail?: LabDetail;
radiologyDetail?: RadiologyDetail;
procedureDetail?: ProcedureDetail;
referralDetail?: ReferralDetail;
nursingDetail?: NursingDetail;
dietDetail?: DietDetail;
activityDetail?: ActivityDetail;
// Supporting
notes?: string;
cdsAlerts: CdsAlert[];
version: number;
createdAt: string;
updatedAt: string;
}

export interface MedicationDetail {
medicationCode: CodeableConcept;
dose: number;
doseUnit: string;
route: string;
frequency: string;
durationDays?: number;
prn: boolean;
dispenseQuantity?: number;
}

export interface LabDetail {
specimenType?: string;
priority: 'routine' | 'urgent' | 'stat';
fastingRequired: boolean;
panels?: CodeableConcept[];
}

export interface RadiologyDetail {
modality: string;
region: string;
laterality?: 'left' | 'right' | 'bilateral';
contrastRequired: boolean;
clinicalIndication?: string;
}

export interface ReferralDetail {
referralType: 'internal' | 'external';
referToFacilityId?: string;
referToSpecialty: string;
urgency: 'routine' | 'urgent' | 'emergency';
clinicalSummary: string;
scheduledAppointmentId?: string;
referralStatus: 'pending_scheduling' | 'scheduled' | 'attended' | 'declined';
}

export interface CdsAlert {
id: string; // cds_ prefix
level: 'info' | 'warning' | 'hard-stop';
ruleId: string;
message: string;
acknowledged: boolean;
acknowledgedBy?: string;
acknowledgedAt?: string;
acknowledgeReason?: string;
}

export interface OrderSet {
id: string; // ords_ prefix
tenantId: string;
name: string;
description?: string;
isGlobal: boolean;
orderTemplates: OrderTemplate[];
createdBy: string;
createdAt: string;
updatedAt: string;
}

export interface OrderTemplate {
orderType: OrderType;
orderCode: CodeableConcept;
priority: OrderPriority;
medicationDetail?: Partial<MedicationDetail>;
labDetail?: Partial<LabDetail>;
radiologyDetail?: Partial<RadiologyDetail>;
}

export interface CodeableConcept {
system: string;
code: string;
display: string;
}

2. PostgreSQL Schema

orders table

CREATE TABLE orders.orders (
id TEXT PRIMARY KEY, -- ord_
tenant_id TEXT NOT NULL,
patient_id TEXT NOT NULL,
encounter_id TEXT NOT NULL,
order_type TEXT NOT NULL CHECK (order_type IN ('medication','laboratory','radiology','procedure','referral','nursing','diet','activity')),
order_code_system TEXT NOT NULL,
order_code TEXT NOT NULL,
order_code_display TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','active','on-hold','completed','cancelled','entered-in-error')),
priority TEXT NOT NULL DEFAULT 'routine' CHECK (priority IN ('routine','urgent','stat','asap')),
ordered_by TEXT NOT NULL,
ordered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
type_detail JSONB, -- type-specific detail (medication/lab/etc.)
referral_detail JSONB, -- separate for indexed queries
notes TEXT,
cds_alerts JSONB NOT NULL DEFAULT '[]',
cancel_reason TEXT,
correction_reason TEXT,
completed_at TIMESTAMPTZ,
version INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_orders_tenant_patient ON orders.orders (tenant_id, patient_id);
CREATE INDEX idx_orders_tenant_encounter ON orders.orders (tenant_id, encounter_id);
CREATE INDEX idx_orders_type_status ON orders.orders (tenant_id, order_type, status);
CREATE INDEX idx_orders_ordered_at ON orders.orders (tenant_id, ordered_at DESC);
CREATE INDEX idx_orders_referral_status ON orders.orders (tenant_id, (referral_detail->>'referralStatus')) WHERE order_type = 'referral';

order_sets table

CREATE TABLE orders.order_sets (
id TEXT PRIMARY KEY, -- ords_
tenant_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
is_global BOOLEAN NOT NULL DEFAULT FALSE,
order_templates JSONB NOT NULL DEFAULT '[]',
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_order_sets_tenant ON orders.order_sets (tenant_id);

outbox table

CREATE TABLE orders.outbox (
id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
subject TEXT NOT NULL,
payload JSONB NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','delivered','failed')),
attempts INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
delivered_at TIMESTAMPTZ
);

CREATE INDEX idx_orders_outbox_pending ON orders.outbox (status, created_at) WHERE status = 'pending';

3. Row-Level Security Policies

ALTER TABLE orders.orders ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON orders.orders
USING (tenant_id = current_setting('app.tenant_id')::TEXT);

-- Apply equivalent policies to order_sets, outbox

4. Allergy Cache Table

Local denormalized cache of patient allergies for CDS checks (populated from REGISTRATION.allergy.recorded events):

CREATE TABLE orders.allergy_cache (
tenant_id TEXT NOT NULL,
patient_id TEXT NOT NULL,
allergen_code TEXT NOT NULL,
allergen_system TEXT NOT NULL,
reaction_type TEXT, -- allergy / intolerance
last_updated_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (tenant_id, patient_id, allergen_code, allergen_system)
);