Mastering Healthcare Data Models: Building an EHR Class Diagram with PlantUML

In the rapidly evolving landscape of Health Information Technology (HIT), precision is not just a preference—it is a regulatory and safety imperative. Electronic Health Record (EHR) systems form the backbone of modern patient care, managing sensitive data ranging from demographic details to complex clinical encounters. However, the complexity of these systems often leads to fragmented documentation and ambiguous data structures that can hinder interoperability.

Mastering Healthcare Data Models: Building an EHR Class Diagram with PlantUML - Real-world system problem context illustration

Visual modeling serves as the bridge between abstract business requirements and concrete software architecture. By using a class diagram to map out the domain entities—such as Patients, Providers, and Clinical Observations—architects can ensure data integrity before a single line of production code is written. This tutorial demonstrates how to leverage PlantUML within VPasCode, a free, browser-based diagram-as-code tool, to create a living, versioned documentation artifact that evolves alongside your healthcare software.

Understanding the Model: Purpose, Scope & Problem Framing

Diagram Abstraction & Representation

A class diagram is the primary tool for modeling the static structure of a system. In the context of an EHR, this diagram defines the “nouns” of the domain: what data exists, how it is grouped, and how it relates to other data. Unlike sequence diagrams that focus on runtime interactions, this class diagram focuses on the schema—the blueprint of the database and object-oriented structure.

Key abstractions include:

  • Entities: Tangible objects like Patient or Provider that store core identity data.
  • Aggregates: Complex objects like Encounter that group related clinical data together.
  • Inheritance: Hierarchies that allow shared attributes, such as the ClinicalItem base class for all medical observations.

Target Domain Scope & Scenario

This model focuses on the core clinical domain of an EHR system. It intentionally excludes billing, scheduling, or external interface layers to maintain clarity on the patient data model. The scope covers the longitudinal record, ensuring that a patient’s history (allergies, problems, medications) is consistently linked to specific clinical encounters.

Key Takeaways & Educational Insights

By constructing this model, you will gain insights into:

  • How to normalize clinical data using inheritance (Generalization).
  • How to distinguish between ownership (Composition) and grouping (Aggregation).
  • How to document cardinality (e.g., one patient has many encounters) for database design.

Complete Diagram & Full Source Code

Below is the finished blueprint of the Electronic Health Record System. This diagram encapsulates the relationships between patients, providers, and the clinical data they generate.

Electronic Health Record System class diagram showing Patient, Encounter, and ClinicalItem relationships

The following code block represents the complete source code for this diagram. You can copy this directly into the VPasCode editor to render it instantly.

@startuml
!include https://static.visual-paradigm.com/web/resources/plantuml-stdlib/themes/vp.puml

title Electronic Health Record System

/'
This class diagram models the core domain of an Electronic Health Record (EHR) system.
The system manages patient demographics, clinical encounters, medical observations,
diagnoses, medications, immunizations, and laboratory results. It also tracks
healthcare providers, care teams, and clinical workflows. The model supports
longitudinal patient records, clinical decision support, and interoperability
with external systems.
'/

class Patient {
  - patientID: UUID
  - name: String
  - dateOfBirth: Date
  - gender: String
  - address: String
  - phone: String
  - emergencyContact: String
  + getMedicalHistory(): List<Encounter>
}

class Encounter {
  - encounterID: UUID
  - encounterDate: DateTime
  - encounterType: String
  - reason: String
  - status: String
  + addDiagnosis(d: Diagnosis)
  + addMedication(m: Medication)
}

class ClinicalItem {
  - itemID: UUID
  - recordedDate: DateTime
  - notes: String
}

class Observation {
  - vitalSign: String
  - value: Double
  - unit: String
  - interpretation: String
}

class Diagnosis {
  - icdCode: String
  - description: String
  - isChronic: Boolean
  - onsetDate: Date
}

class Medication {
  - rxNormCode: String
  - name: String
  - dosage: String
  - route: String
  - startDate: Date
  - endDate: Date
}

class Immunization {
  - vaccineCode: String
  - vaccineName: String
  - doseNumber: Integer
  - administrationDate: Date
}

class LabResult {
  - loincCode: String
  - testName: String
  - resultValue: String
  - referenceRange: String
  - status: String
}

class Provider {
  - providerID: UUID
  - name: String
  - specialty: String
  - npiNumber: String
  - department: String
}

class CareTeam {
  - teamID: UUID
  - teamName: String
  - leadProvider: Provider
  + addMember(p: Provider)
  + removeMember(p: Provider)
}

class ClinicalDocument {
  - documentID: UUID
  - title: String
  - createdDate: DateTime
  - content: Text
  - documentType: String
}

class Allergy {
  - allergyID: UUID
  - allergen: String
  - reaction: String
  - severity: String
  - onsetDate: Date
}

class ProblemList {
  - problemID: UUID
  - condition: String
  - status: String
  - dateRecorded: Date
}

' Generalization (inheritance)
ClinicalItem <|-- Observation
ClinicalItem <|-- Diagnosis
ClinicalItem <|-- Medication
ClinicalItem <|-- Immunization
ClinicalItem <|-- LabResult
ClinicalItem <|-- Allergy
ClinicalItem <|-- ProblemList

' Composition (encounter owns clinical items)
Encounter *-- ClinicalItem : contains

' Aggregation (care team groups providers)
CareTeam o-- Provider : has members

' Association (patient has encounters)
Patient "1" -- "0..*" Encounter : participates in

' Association (provider responsible for encounter)
Encounter "0..*" -- "1" Provider : conducted by

' Association (patient has allergies)
Patient "1" -- "0..*" Allergy : has

' Association (patient has problem list)
Patient "1" -- "0..*" ProblemList : has

' Association (clinical document belongs to encounter)
Encounter "1" -- "0..*" ClinicalDocument : documents

' Association (care team assigned to patient)
Patient "1" -- "0..*" CareTeam : assigned to

' Association (provider documents)
Provider "1" -- "0..*" ClinicalDocument : author
@enduml

Step-by-Step Architectural Walkthrough

Building a robust class diagram requires a phased approach. We will construct this EHR model in four logical stages, ensuring each relationship is semantically correct before moving to the next.

Phase 1: Canvas Configuration & Layout Directives

Before defining classes, we set the stage. We include the Visual Paradigm theme to ensure the diagram looks professional and consistent with enterprise standards. We also define the title and a comment block to provide context for future readers.

Start with the include directive and metadata:

!include https://static.visual-paradigm.com/web/resources/plantuml-stdlib/themes/vp.puml

title Electronic Health Record System

'/
This class diagram models the core domain of an Electronic Health Record (EHR) system...
'/

Phase 2: Declaring Core Entities, Actors, and Boundaries

The foundation of any EHR is the Patient and the Provider. These are the primary actors in the system. Define their attributes (private fields) and methods (public operations). In PlantUML, use - for private attributes and + for public methods.

class Patient {
  - patientID: UUID
  - name: String
  + getMedicalHistory(): List
}

class Provider {
  - providerID: UUID
  - name: String
  - specialty: String
}

Phase 3: Mapping Data Flows & Key Interactions

The Encounter class acts as the central hub for clinical data. It links the patient and provider to specific medical events. We define relationships to capture the “who, when, and what” of the visit.

Define the associations:

Patient "1" -- "0..*" Encounter : participates in
Encounter "0..*" -- "1" Provider : conducted by

Phase 4: Grouping, Annotations & Visual Polish

To reduce redundancy, we introduce the ClinicalItem abstract class. This allows us to use Generalization (inheritance) for all medical records like Diagnosis, Medication, and LabResult. We also define Composition to show that an Encounter owns its clinical items (they cannot exist without the encounter).

ClinicalItem <|-- Diagnosis
Encounter *-- ClinicalItem : contains

Syntax & Keyword Deep Dive

Understanding the specific PlantUML syntax is crucial for mastering class diagrams. Here are the key keywords used in this EHR model:

  • class: Defines a new class structure with its name and members.
  • <|--: Represents Generalization (inheritance). The arrow points to the parent class (e.g., ClinicalItem).
  • *--: Represents Composition. The filled diamond indicates strong ownership (e.g., Encounter owns ClinicalItem).
  • o--: Represents Aggregation. The hollow diamond indicates a "has-a" relationship where the child can exist independently (e.g., CareTeam has Provider).
  • --: Represents a standard Association. A line connecting two classes without specific ownership semantics.
  • "0..*": Specifies Cardinality. This notation indicates that one side can have zero or more instances on the other side.
  • /' ... '/: Defines a comment block that renders as a note in the diagram, useful for documentation.

Best Practices & Pitfalls to Avoid

When modeling complex healthcare systems, clarity is paramount. Follow these best practices to maintain a clean and scalable diagram:

  1. Use Abstract Base Classes: As seen with ClinicalItem, grouping similar entities reduces visual clutter and enforces domain logic.
  2. Be Precise with Cardinality: Avoid vague relationships. Explicitly state "1" vs "0..*" to prevent database design errors.
  3. Separate Concerns: Keep administrative data (billing) separate from clinical data (encounters) in your mental model, even if they are in the same diagram.
  4. Keep it Readable: Use meaningful attribute names (e.g., icdCode instead of code1) to ensure the diagram serves as documentation.

Start Building PlantUML Class Diagrams Faster with VPasCode

Test, preview, and customize this EHR class diagram online in VPasCode without installing any tools or configuring local environments.

Scroll to Top