Case Study 2 — A Complete Capstone: The Healthcare Clinic
A second worked capstone — the healthcare clinic domain — which raises the stakes on integrity, security, and privacy. It shows how the same assembly applies to a domain where correctness and PII handling are paramount.
1. Requirements & questions
A clinic tracks patients, providers, appointments, prescriptions, and visits. Questions: a patient's appointment history? a provider's schedule for a day? which appointments are upcoming/no-show? active prescriptions per patient? appointment volume per provider per month? It must protect patient PII (HIPAA-style obligations — Chapter 32).
2. Data model (ER)
Entities: patient, provider, appointment, visit, prescription, medication, diagnosis. Relationships: patient→appointments and provider→appointments are 1:N; an appointment may produce a visit; a visit has many prescriptions (each referencing a medication) and diagnoses — patient↔medication is effectively M:N through prescriptions. A scheduling constraint: a provider can't have two overlapping appointments (a range/exclusion-constraint candidate — Chapter 16).
3. Schema (DDL) — integrity is critical here
CREATE TABLE patients (
patient_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
dob date NOT NULL,
email text UNIQUE, -- PII
phone text, -- PII
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE providers (
provider_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL, specialty text NOT NULL
);
CREATE TABLE appointments (
appointment_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id integer NOT NULL REFERENCES patients(patient_id),
provider_id integer NOT NULL REFERENCES providers(provider_id),
scheduled_at timestamptz NOT NULL,
duration_min integer NOT NULL CHECK (duration_min > 0),
status text NOT NULL DEFAULT 'scheduled'
CHECK (status IN ('scheduled','completed','cancelled','no_show'))
);
CREATE TABLE prescriptions (
prescription_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
visit_id integer NOT NULL REFERENCES visits(visit_id) ON DELETE CASCADE,
medication_id integer NOT NULL REFERENCES medications(medication_id),
dosage text NOT NULL, start_date date NOT NULL, end_date date,
CHECK (end_date IS NULL OR end_date >= start_date)
);
Heavy use of constraints (this domain demands integrity): status domains, positive duration, date ordering, required references. Overlapping-appointment prevention can use a PostGIS-free range exclusion constraint on (provider_id, tstzrange(scheduled_at, scheduled_at + duration)) (Chapter 16) — so the database itself forbids double-booking a provider.
4–5. Data & core queries
Seed realistic patients/providers/appointments; a generator for volume. Core queries:
-- A patient's appointment history
SELECT a.scheduled_at, p.name AS provider, a.status
FROM appointments a JOIN providers p ON p.provider_id=a.provider_id
WHERE a.patient_id = :pid ORDER BY a.scheduled_at DESC;
-- Provider's schedule for a day (half-open range, Ch. 8)
SELECT a.scheduled_at, pt.name AS patient, a.duration_min
FROM appointments a JOIN patients pt ON pt.patient_id=a.patient_id
WHERE a.provider_id = :prov
AND a.scheduled_at >= :day AND a.scheduled_at < :day + interval '1 day'
ORDER BY a.scheduled_at;
-- No-show rate per provider per month (aggregation)
SELECT p.name, date_trunc('month', a.scheduled_at) AS month,
COUNT(*) FILTER (WHERE a.status='no_show')::numeric / COUNT(*) AS no_show_rate
FROM appointments a JOIN providers p ON p.provider_id=a.provider_id
GROUP BY p.name, month ORDER BY month;
(Note COUNT(*) FILTER (...) for the conditional rate, and half-open date ranges — Chapters 7–8.)
6–8. Indexes, transactions, views
- Indexes:
appointments(patient_id),appointments(provider_id, scheduled_at)(the schedule query),prescriptions(visit_id)— justified andEXPLAIN-verified. - Transaction: "complete a visit" = mark the appointment completed, create the visit, insert prescriptions and diagnoses — all atomic (Ch. 26).
- Views: an
upcoming_appointmentsview; apatient_summaryview that excludes sensitive columns for staff who shouldn't see full PII.
9. Security & privacy (elevated here — Ch. 32)
This domain makes Chapter 32 concrete:
- Least-privilege roles: front-desk staff (scheduling, no clinical data), clinicians (full clinical access), billing (financial only) — each granted the minimum.
- Hide PII with views/column grants: the front-desk role sees a view without diagnoses/full contact details.
- Row-level security could restrict a provider to their own patients.
- Encryption in transit (TLS) required; sensitive fields candidates for column-level encryption.
- Audit logging (trigger-based, Ch. 21) on access to clinical records.
- Right-to-erasure planned: how to delete a patient's PII while preserving anonymized aggregates (interacts with FKs and audit logs — a documented design decision).
10–12. App layer, operations, documentation
- App layer (Ch. 29): a parameterized repository; transactions for multi-step clinical operations; specific error handling.
- Operations (Ch. 38): tested backups (with the extra weight that medical data loss is catastrophic), monitoring.
- Documentation: README, data dictionary, query catalog, and a design-decisions doc emphasizing the integrity and privacy choices (why these constraints, the double-booking exclusion constraint, the role design, the PII/erasure plan, why PostgreSQL).
The result — and why it impresses
A clinic database where the constraints and security model are the stars: the database structurally prevents double-booking, invalid statuses, and unauthorized PII access, and the design-decisions doc reasons about privacy law. For a domain where correctness and confidentiality are non-negotiable, demonstrating that you made integrity and security structural — not hopeful — is exactly the judgment employers in regulated industries want.
Discussion questions
- Why does this domain warrant more constraints and stricter security than, say, a blog?
- How does an exclusion constraint prevent provider double-booking, and why is that better than app-code checks?
- How do least-privilege roles + views enforce that front-desk staff can't see clinical PII?
- What makes "right to erasure" tricky here (FKs, audit logs), and how would you design for it?
- ⭐ Compare this capstone's emphasis (integrity/security) to the library capstone's (queries/operations). How does the domain shape what your portfolio piece should foreground?