Hearts in Scrubs is a full clinical practice management system built for a South African wound care specialist. It runs an entire single-practitioner practice from one place: patient records, wound assessments captured at the bedside, an AI clinical scribe, a WhatsApp patient bot, invoicing, and medical aid claims.
This case study explores the technical architecture that lets one developer build and maintain a complete clinical system — and ship changes the same week a nurse asks for them — using LEGO Builder principles.
The Challenge
A wound care practice has a very specific shape, and most off-the-shelf practice-management software fights against it:
- Bedside capture: a wound care nurse is on her feet all day. Assessments have to be captured on a phone or iPad at the point of care — not typed up afterhours at a desk.
- Wound history over time: healing is a timeline. Every assessment needs measurements, tissue type, exudate, and photos, all comparable visit-to-visit to show whether a wound is improving.
- POPIA & clinical confidentiality: patient records and wound photographs are special-category personal information under South Africa's Protection of Personal Information Act.
- Medical aid claims: billing isn't a flat invoice — it's ICD-10 diagnosis codes, procedure and tariff codes, and electronic claims submitted to South African medical schemes.
- One practitioner, no IT department: the whole system has to be operable by a single clinician and maintainable by a single developer.
Technical Architecture
Data Model
At the centre is the wound assessment — a structured, photo-backed snapshot of a wound at a moment in time, always tied to a patient and a visit:
// models/wound-assessment.js — one block, one responsibility
export const WoundAssessmentModel = {
tableName: 'wound_assessments',
fields: {
id: 'uuid',
patientId: 'uuid',
visitId: 'uuid',
site: 'string', // anatomical location of the wound
lengthMm: 'integer',
widthMm: 'integer',
depthMm: 'integer',
tissueType: 'enum', // granulating, sloughy, necrotic, epithelialising
exudate: 'enum', // none, low, moderate, high
photos: 'jsonb', // references to encrypted photo storage
notes: 'text',
assessedAt: 'timestamp'
}
};
Bedside Capture, Instant Sync
The nurse captures an assessment on her phone; it is validated and persisted as a single composed block, so the full wound history is available on any device seconds later:
// controllers/assessment-controller.js — orchestration only
export async function recordWoundAssessment(input) {
ensureClinician(input.actor); // guards/
const assessment = buildWoundAssessment(input); // builders/
const photos = await storeEncryptedPhotos(input.photos); // bridges/
const saved = await saveAssessment({ ...assessment, photos });
await refreshWoundTimeline(saved.patientId, saved.site); // processors/
return saved;
}
WhatsApp Patient Bot
Appointment reminders and check-ins go out over Meta's WhatsApp Cloud API directly — no third-party gateway, no per-message markup:
// bridges/whatsapp-bridge.js — the only place that talks to Meta
export async function sendAppointmentReminder(patient, appointment) {
return whatsappCloudApi.send({
to: patient.whatsappNumber,
template: 'appointment_reminder',
variables: {
name: patient.firstName,
date: formatDate(appointment.startsAt), // utilities/
time: formatTime(appointment.startsAt)
}
});
}
AI Clinical Scribe
The nurse dictates; a processor turns free speech into a structured clinical note she reviews and signs off — she never has to type at a desk after hours:
// processors/clinical-scribe.js — turns dictation into a draft note
export async function draftClinicalNote(audio, context) {
const transcript = await transcribe(audio); // bridges/
const structured = extractClinicalFields(transcript); // utilities/
return buildNote({
patientId: context.patientId,
visitId: context.visitId,
subjective: structured.subjective,
objective: structured.objective,
plan: structured.plan,
status: 'draft' // always clinician-reviewed before it is final
});
}
LEGO Builder Implementation
Block Organisation
The whole codebase is organised into the same small, predictable categories, so any feature is assembled from blocks that already exist:
src/
├── utilities/ // Pure functions (date/measurement formatting, ICD-10 lookups)
├── builders/ // Object constructors (assessment, note, claim builders)
├── guards/ // Auth, role, and POPIA-consent checks
├── processors/ // Clinical logic (wound timeline, scribe, claim assembly)
├── bridges/ // External integrations (WhatsApp Cloud API, transcription, medical-aid switch)
└── controllers/ // Request handlers and orchestration
Example: Building a Medical Aid Claim
A claim is assembled from blocks — diagnosis codes, procedure codes, and the consultation — then checked by a guard before it is ever sent to the scheme:
// controllers/claim-controller.js
export async function submitMedicalAidClaim(visitId) {
const visit = await getVisit(visitId);
const claim = buildClaim({ // builders/
member: visit.patient.medicalAid,
diagnoses: visit.icd10Codes,
procedures: visit.tariffCodes,
consultation: visit.consultation
});
ensureClaimIsComplete(claim); // guards/ — throws on missing codes
return await submitToScheme(claim); // bridges/
}
Why It Holds Together
Because every block does one thing, the parts that touch patient data stay small and easy to reason about:
- Guards isolate consent and access: POPIA and role checks live in one place, not scattered through controllers.
- Bridges isolate the outside world: WhatsApp, transcription, and the medical-aid switch each have exactly one integration point that can be swapped without touching clinical logic.
- Photos are encrypted at the boundary: wound images are stored encrypted, handled by a single bridge rather than passed around in application code.
Results and Impact
The architecture exists to serve one outcome — the practitioner's day:
- Documentation happens at the bedside: assessments are captured on a phone or iPad during the visit and sync instantly, instead of being written up after hours.
- Full history in seconds: a patient's complete wound history, photos, and invoices are one tap away on any device.
- Direct support: because one developer knows every block, a feature request is usually live the same week — not parked on a roadmap.
- Built to be owned: clean, maintainable code with no AI dependency after delivery.
Lessons Learned
1. Build for where the work actually happens
The single biggest design decision was mobile-first bedside capture. Everything else — sync, timeline, photos — follows from the fact that a wound care nurse works on her feet, not at a desk.
2. Controllers should orchestrate, not implement
Controllers that just compose guards, builders, processors, and bridges read like a recipe. The clinical logic lives in small processors that are easy to test in isolation.
3. Put every integration behind one bridge
WhatsApp, transcription, and medical-aid submission each have a single integration point. When an API changes, exactly one block changes with it.
4. Keep sensitive logic small
POPIA consent, access control, and photo encryption are concentrated in guards and bridges — small, auditable blocks rather than rules sprinkled across the codebase.
Conclusion
Hearts in Scrubs shows that LEGO Builder architecture scales from a single utility function to a complete clinical practice management system — patient records, wound assessments, an AI scribe, a WhatsApp bot, billing, and medical aid claims — built and maintained by one developer.
By treating every piece of code as a reusable block with a single responsibility, the system stays fast to change and easy to maintain: exactly what a busy wound care practice needs.
← Back to Blog