Care Plan Service — Data Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
ID Prefix Registry (this service)
| Prefix | Entity |
|---|---|
cp_ | CarePlan |
cpg_ | CarePlanGoal |
cpa_ | CarePlanActivity |
ctm_ | CareTeamMember |
All IDs are ULIDs (26-char, lexicographically sortable, millisecond precision).
TypeScript Interfaces
export interface CarePlan {
id: CarePlanId; // ULID with prefix cp_
tenantId: TenantId;
patientId: string; // pat_ ULID from registration-service
encounterId?: string; // enc_ ULID from scheduling-service
title: string;
description?: string;
status: CarePlanStatus;
category?: CodeableConcept;
version: number; // optimistic concurrency
createdBy: string; // usr_ ULID
createdAt: Date;
updatedAt: Date;
lastReviewedAt?: Date;
nextReviewDue?: Date;
goals?: CarePlanGoal[];
activities?: CarePlanActivity[];
careTeamMembers?: CareTeamMember[];
}
export interface CarePlanGoal {
id: GoalId; // cpg_
tenantId: TenantId;
carePlanId: CarePlanId;
patientId: string;
description: string;
targetDetail?: TargetDetail;
dueAt?: Date;
status: GoalStatus;
createdBy: string;
createdAt: Date;
updatedAt: Date;
}
export interface CarePlanActivity {
id: ActivityId; // cpa_
tenantId: TenantId;
carePlanId: CarePlanId;
description: string;
assigneeId?: string; // prac_ ULID from provider-directory-service
scheduleDetail?: unknown; // JSON — repeat/timing
status: ActivityStatus;
completedAt?: Date;
completionNote?: string;
createdBy: string;
createdAt: Date;
updatedAt: Date;
}
export interface CareTeamMember {
id: CareTeamMemberId; // ctm_
tenantId: TenantId;
carePlanId: CarePlanId;
practitionerId: string; // prac_ ULID
role: CodeableConcept;
effectiveFrom: Date;
effectiveTo?: Date;
createdAt: Date;
}
Postgres Schema
-- ============================================================
-- care_plans
-- ============================================================
CREATE TABLE care_plans (
id TEXT PRIMARY KEY, -- cp_ULID
tenant_id TEXT NOT NULL,
patient_id TEXT NOT NULL,
encounter_id TEXT,
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'draft',
category JSONB,
version INTEGER NOT NULL DEFAULT 1,
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
last_reviewed_at TIMESTAMPTZ,
next_review_due DATE
);
CREATE INDEX ix_care_plans_tenant_patient ON care_plans (tenant_id, patient_id);
CREATE INDEX ix_care_plans_tenant_status ON care_plans (tenant_id, status);
CREATE INDEX ix_care_plans_patient_status ON care_plans (patient_id, status);
ALTER TABLE care_plans ENABLE ROW LEVEL SECURITY;
CREATE POLICY care_plans_tenant_isolation ON care_plans
USING (tenant_id = current_setting('app.current_tenant_id'));
-- ============================================================
-- care_plan_goals
-- ============================================================
CREATE TABLE care_plan_goals (
id TEXT PRIMARY KEY, -- cpg_ULID
tenant_id TEXT NOT NULL,
care_plan_id TEXT NOT NULL REFERENCES care_plans(id),
patient_id TEXT NOT NULL,
description TEXT NOT NULL,
target_detail JSONB,
due_at DATE,
status TEXT NOT NULL DEFAULT 'proposed',
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_care_plan_goals_care_plan_id ON care_plan_goals (care_plan_id);
CREATE INDEX ix_care_plan_goals_tenant_patient ON care_plan_goals (tenant_id, patient_id);
ALTER TABLE care_plan_goals ENABLE ROW LEVEL SECURITY;
CREATE POLICY care_plan_goals_tenant_isolation ON care_plan_goals
USING (tenant_id = current_setting('app.current_tenant_id'));
-- ============================================================
-- care_plan_activities
-- ============================================================
CREATE TABLE care_plan_activities (
id TEXT PRIMARY KEY, -- cpa_ULID
tenant_id TEXT NOT NULL,
care_plan_id TEXT NOT NULL REFERENCES care_plans(id),
description TEXT NOT NULL,
assignee_id TEXT,
schedule_detail JSONB,
status TEXT NOT NULL DEFAULT 'not_started',
completed_at TIMESTAMPTZ,
completion_note TEXT,
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_care_plan_activities_care_plan_id ON care_plan_activities (care_plan_id);
CREATE INDEX ix_care_plan_activities_tenant_status ON care_plan_activities (tenant_id, status);
ALTER TABLE care_plan_activities ENABLE ROW LEVEL SECURITY;
CREATE POLICY care_plan_activities_tenant_isolation ON care_plan_activities
USING (tenant_id = current_setting('app.current_tenant_id'));
-- ============================================================
-- care_team_members
-- ============================================================
CREATE TABLE care_team_members (
id TEXT PRIMARY KEY, -- ctm_ULID
tenant_id TEXT NOT NULL,
care_plan_id TEXT NOT NULL REFERENCES care_plans(id),
practitioner_id TEXT NOT NULL,
role JSONB NOT NULL,
effective_from DATE NOT NULL,
effective_to DATE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ix_care_team_members_care_plan_id ON care_team_members (care_plan_id);
CREATE INDEX ix_care_team_members_practitioner ON care_team_members (tenant_id, practitioner_id);
ALTER TABLE care_team_members ENABLE ROW LEVEL SECURITY;
CREATE POLICY care_team_members_tenant_isolation ON care_team_members
USING (tenant_id = current_setting('app.current_tenant_id'));
-- ============================================================
-- outbox (transactional outbox pattern)
-- ============================================================
CREATE TABLE outbox (
id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
published_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'pending'
);
CREATE INDEX ix_outbox_pending ON outbox (status, created_at) WHERE status = 'pending';
Migrations
| Migration file | Description |
|---|---|
20260418000001_create_care_plans.sql | Initial tables: care_plans, care_plan_goals, care_plan_activities, care_team_members, outbox |
20260418000002_add_rls_policies.sql | Enable RLS on all tables |
20260418000003_add_next_review_due.sql | Add next_review_due column to care_plans |