Complete Patient Management System
A comprehensive example demonstrating real-world FHIR patterns. This implementation showcases production-ready patterns including patient registration, vital signs recording, and comprehensive record retrieval. The code demonstrates proper use of transaction bundles, the $everything operation, and LOINC-coded observations following clinical best practices.
import java.util.*;
import java.util.stream.Collectors;
public class PatientManagementSystem {
private final IGenericClient client;
private final FhirContext ctx;
public PatientManagementSystem(String serverBase) {
this.ctx = FhirContext.forR4();
this.client = ctx.newRestfulGenericClient(serverBase);
}
// Register new patient
public String registerPatient(PatientRegistrationRequest request) {
Patient patient = new Patient();
// Set identifiers
patient.addIdentifier()
.setSystem("http://hospital.org/mrn")
.setValue(generateMRN())
.setUse(Identifier.IdentifierUse.OFFICIAL);
if (request.getSsn() != null) {
patient.addIdentifier()
.setSystem("http://hl7.org/fhir/sid/us-ssn")
.setValue(request.getSsn());
}
// Set demographics
patient.addName()
.setFamily(request.getLastName())
.addGiven(request.getFirstName())
.addGiven(request.getMiddleName())
.setUse(HumanName.NameUse.OFFICIAL);
patient.setGender(request.getGender());
patient.setBirthDate(request.getBirthDate());
// Set contact information
if (request.getPhone() != null) {
patient.addTelecom()
.setSystem(ContactPoint.ContactPointSystem.PHONE)
.setValue(request.getPhone())
.setUse(ContactPoint.ContactPointUse.MOBILE);
}
if (request.getEmail() != null) {
patient.addTelecom()
.setSystem(ContactPoint.ContactPointSystem.EMAIL)
.setValue(request.getEmail());
}
// Set address
if (request.getAddress() != null) {
patient.addAddress()
.setUse(Address.AddressUse.HOME)
.addLine(request.getAddress().getStreet())
.setCity(request.getAddress().getCity())
.setState(request.getAddress().getState())
.setPostalCode(request.getAddress().getZipCode())
.setCountry("USA");
}
// Set marital status
if (request.getMaritalStatus() != null) {
patient.setMaritalStatus(new CodeableConcept()
.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-MaritalStatus")
.setCode(request.getMaritalStatus()));
}
// Add emergency contact
if (request.getEmergencyContact() != null) {
Patient.ContactComponent contact = patient.addContact();
contact.addRelationship()
.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v2-0131")
.setCode("C")
.setDisplay("Emergency Contact");
contact.setName(new HumanName()
.setText(request.getEmergencyContact().getName()));
contact.addTelecom()
.setSystem(ContactPoint.ContactPointSystem.PHONE)
.setValue(request.getEmergencyContact().getPhone());
}
patient.setActive(true);
// Create patient
MethodOutcome outcome = client.create()
.resource(patient)
.execute();
return outcome.getId().getIdPart();
}
// Get comprehensive patient record
public PatientRecord getPatientRecord(String patientId) {
// Use $everything operation
Bundle everything = client.operation()
.onInstance(new IdType("Patient", patientId))
.named("$everything")
.withNoParameters(Parameters.class)
.returnResourceType(Bundle.class)
.execute();
PatientRecord record = new PatientRecord();
for (Bundle.BundleEntryComponent entry : everything.getEntry()) {
Resource resource = entry.getResource();
if (resource instanceof Patient) {
record.setPatient((Patient) resource);
} else if (resource instanceof Condition) {
record.addCondition((Condition) resource);
} else if (resource instanceof Observation) {
record.addObservation((Observation) resource);
} else if (resource instanceof MedicationRequest) {
record.addMedication((MedicationRequest) resource);
} else if (resource instanceof AllergyIntolerance) {
record.addAllergy((AllergyIntolerance) resource);
} else if (resource instanceof Procedure) {
record.addProcedure((Procedure) resource);
} else if (resource instanceof Encounter) {
record.addEncounter((Encounter) resource);
}
}
return record;
}
// Record vital signs
public void recordVitalSigns(String patientId, VitalSignsData vitals) {
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
Date effectiveTime = new Date();
String encounterId = vitals.getEncounterId();
// Blood Pressure
if (vitals.getSystolic() != null && vitals.getDiastolic() != null) {
Observation bp = createBloodPressureObservation(
patientId, vitals.getSystolic(), vitals.getDiastolic(),
effectiveTime, encounterId);
bundle.addEntry()
.setResource(bp)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
}
// Heart Rate
if (vitals.getHeartRate() != null) {
Observation hr = createSimpleObservation(
patientId, "8867-4", "Heart rate",
vitals.getHeartRate(), "beats/minute",
effectiveTime, encounterId);
bundle.addEntry()
.setResource(hr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
}
// Temperature
if (vitals.getTemperature() != null) {
Observation temp = createSimpleObservation(
patientId, "8310-5", "Body temperature",
vitals.getTemperature(), "Cel",
effectiveTime, encounterId);
bundle.addEntry()
.setResource(temp)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
}
// Respiratory Rate
if (vitals.getRespiratoryRate() != null) {
Observation rr = createSimpleObservation(
patientId, "9279-1", "Respiratory rate",
vitals.getRespiratoryRate(), "breaths/minute",
effectiveTime, encounterId);
bundle.addEntry()
.setResource(rr)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
}
// Oxygen Saturation
if (vitals.getOxygenSaturation() != null) {
Observation spo2 = createSimpleObservation(
patientId, "2708-6", "Oxygen saturation",
vitals.getOxygenSaturation(), "%",
effectiveTime, encounterId);
bundle.addEntry()
.setResource(spo2)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
}
// Execute transaction
client.transaction().withBundle(bundle).execute();
}
private Observation createBloodPressureObservation(
String patientId, int systolic, int diastolic,
Date effectiveTime, String encounterId) {
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
obs.addCategory()
.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("vital-signs");
obs.setCode(new CodeableConcept()
.addCoding()
.setSystem("http://loinc.org")
.setCode("85354-9")
.setDisplay("Blood pressure panel"));
obs.setSubject(new Reference("Patient/" + patientId));
obs.setEffective(new DateTimeType(effectiveTime));
if (encounterId != null) {
obs.setEncounter(new Reference("Encounter/" + encounterId));
}
// Systolic component
obs.addComponent()
.setCode(new CodeableConcept()
.addCoding()
.setSystem("http://loinc.org")
.setCode("8480-6")
.setDisplay("Systolic blood pressure"))
.setValue(new Quantity()
.setValue(systolic)
.setUnit("mmHg")
.setSystem("http://unitsofmeasure.org")
.setCode("mm[Hg]"));
// Diastolic component
obs.addComponent()
.setCode(new CodeableConcept()
.addCoding()
.setSystem("http://loinc.org")
.setCode("8462-4")
.setDisplay("Diastolic blood pressure"))
.setValue(new Quantity()
.setValue(diastolic)
.setUnit("mmHg")
.setSystem("http://unitsofmeasure.org")
.setCode("mm[Hg]"));
return obs;
}
private Observation createSimpleObservation(
String patientId, String loincCode, String display,
Number value, String unit,
Date effectiveTime, String encounterId) {
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
obs.addCategory()
.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("vital-signs");
obs.setCode(new CodeableConcept()
.addCoding()
.setSystem("http://loinc.org")
.setCode(loincCode)
.setDisplay(display));
obs.setSubject(new Reference("Patient/" + patientId));
obs.setEffective(new DateTimeType(effectiveTime));
if (encounterId != null) {
obs.setEncounter(new Reference("Encounter/" + encounterId));
}
obs.setValue(new Quantity()
.setValue(value.doubleValue())
.setUnit(unit)
.setSystem("http://unitsofmeasure.org"));
return obs;
}
// Search patients
public List<PatientSummary> searchPatients(PatientSearchCriteria criteria) {
IQuery<Bundle> query = client.search().forResource(Patient.class);
if (criteria.getLastName() != null) {
query = query.where(Patient.FAMILY.matches().value(criteria.getLastName()));
}
if (criteria.getFirstName() != null) {
query = query.where(Patient.GIVEN.matches().value(criteria.getFirstName()));
}
if (criteria.getBirthDate() != null) {
query = query.where(Patient.BIRTHDATE.exactly().day(criteria.getBirthDate()));
}
if (criteria.getGender() != null) {
query = query.where(Patient.GENDER.exactly().code(criteria.getGender().toCode()));
}
if (criteria.getIdentifier() != null) {
query = query.where(Patient.IDENTIFIER.exactly()
.identifier(criteria.getIdentifier()));
}
Bundle results = query
.count(20)
.returnBundle(Bundle.class)
.execute();
return results.getEntry().stream()
.map(entry -> {
Patient patient = (Patient) entry.getResource();
return new PatientSummary(patient);
})
.collect(Collectors.toList());
}
private String generateMRN() {
return "MRN-" + System.currentTimeMillis();
}
// Supporting classes
public static class PatientRegistrationRequest {
private String firstName;
private String middleName;
private String lastName;
private String ssn;
private Enumerations.AdministrativeGender gender;
private Date birthDate;
private String phone;
private String email;
private AddressInfo address;
private String maritalStatus;
private EmergencyContactInfo emergencyContact;
// Getters and setters
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getMiddleName() { return middleName; }
public void setMiddleName(String middleName) { this.middleName = middleName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getSsn() { return ssn; }
public void setSsn(String ssn) { this.ssn = ssn; }
public Enumerations.AdministrativeGender getGender() { return gender; }
public void setGender(Enumerations.AdministrativeGender gender) { this.gender = gender; }
public Date getBirthDate() { return birthDate; }
public void setBirthDate(Date birthDate) { this.birthDate = birthDate; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public AddressInfo getAddress() { return address; }
public void setAddress(AddressInfo address) { this.address = address; }
public String getMaritalStatus() { return maritalStatus; }
public void setMaritalStatus(String maritalStatus) { this.maritalStatus = maritalStatus; }
public EmergencyContactInfo getEmergencyContact() { return emergencyContact; }
public void setEmergencyContact(EmergencyContactInfo ec) { this.emergencyContact = ec; }
}
public static class AddressInfo {
private String street;
private String city;
private String state;
private String zipCode;
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getZipCode() { return zipCode; }
public void setZipCode(String zipCode) { this.zipCode = zipCode; }
}
public static class EmergencyContactInfo {
private String name;
private String phone;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
public static class VitalSignsData {
private Integer systolic;
private Integer diastolic;
private Integer heartRate;
private Double temperature;
private Integer respiratoryRate;
private Integer oxygenSaturation;
private String encounterId;
public Integer getSystolic() { return systolic; }
public void setSystolic(Integer systolic) { this.systolic = systolic; }
public Integer getDiastolic() { return diastolic; }
public void setDiastolic(Integer diastolic) { this.diastolic = diastolic; }
public Integer getHeartRate() { return heartRate; }
public void setHeartRate(Integer heartRate) { this.heartRate = heartRate; }
public Double getTemperature() { return temperature; }
public void setTemperature(Double temperature) { this.temperature = temperature; }
public Integer getRespiratoryRate() { return respiratoryRate; }
public void setRespiratoryRate(Integer rr) { this.respiratoryRate = rr; }
public Integer getOxygenSaturation() { return oxygenSaturation; }
public void setOxygenSaturation(Integer spo2) { this.oxygenSaturation = spo2; }
public String getEncounterId() { return encounterId; }
public void setEncounterId(String encounterId) { this.encounterId = encounterId; }
}
public static class PatientRecord {
private Patient patient;
private List<Condition> conditions = new ArrayList<>();
private List<Observation> observations = new ArrayList<>();
private List<MedicationRequest> medications = new ArrayList<>();
private List<AllergyIntolerance> allergies = new ArrayList<>();
private List<Procedure> procedures = new ArrayList<>();
private List<Encounter> encounters = new ArrayList<>();
public Patient getPatient() { return patient; }
public void setPatient(Patient patient) { this.patient = patient; }
public List<Condition> getConditions() { return conditions; }
public List<Observation> getObservations() { return observations; }
public List<MedicationRequest> getMedications() { return medications; }
public List<AllergyIntolerance> getAllergies() { return allergies; }
public List<Procedure> getProcedures() { return procedures; }
public List<Encounter> getEncounters() { return encounters; }
public void addCondition(Condition c) { conditions.add(c); }
public void addObservation(Observation o) { observations.add(o); }
public void addMedication(MedicationRequest m) { medications.add(m); }
public void addAllergy(AllergyIntolerance a) { allergies.add(a); }
public void addProcedure(Procedure p) { procedures.add(p); }
public void addEncounter(Encounter e) { encounters.add(e); }
}
public static class PatientSearchCriteria {
private String lastName;
private String firstName;
private Date birthDate;
private Enumerations.AdministrativeGender gender;
private String identifier;
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public Date getBirthDate() { return birthDate; }
public void setBirthDate(Date birthDate) { this.birthDate = birthDate; }
public Enumerations.AdministrativeGender getGender() { return gender; }
public void setGender(Enumerations.AdministrativeGender gender) { this.gender = gender; }
public String getIdentifier() { return identifier; }
public void setIdentifier(String identifier) { this.identifier = identifier; }
}
public static class PatientSummary {
private String id;
private String name;
private Enumerations.AdministrativeGender gender;
private Date birthDate;
private String mrn;
public PatientSummary(Patient patient) {
this.id = patient.getIdElement().getIdPart();
this.name = patient.getNameFirstRep().getNameAsSingleString();
this.gender = patient.getGender();
this.birthDate = patient.getBirthDate();
patient.getIdentifier().stream()
.filter(id -> id.getSystem() != null && id.getSystem().contains("mrn"))
.findFirst()
.ifPresent(id -> this.mrn = id.getValue());
}
public String getId() { return id; }
public String getName() { return name; }
public Enumerations.AdministrativeGender getGender() { return gender; }
public Date getBirthDate() { return birthDate; }
public String getMrn() { return mrn; }
}
}
Common LOINC Codes for Vital Signs
| Vital Sign | LOINC Code | Display |
|---|---|---|
| Blood Pressure Panel | 85354-9 | Blood pressure panel |
| Systolic BP | 8480-6 | Systolic blood pressure |
| Diastolic BP | 8462-4 | Diastolic blood pressure |
| Heart Rate | 8867-4 | Heart rate |
| Respiratory Rate | 9279-1 | Respiratory rate |
| Body Temperature | 8310-5 | Body temperature |
| Oxygen Saturation | 2708-6 | Oxygen saturation |
| Body Weight | 29463-7 | Body weight |
| Body Height | 8302-2 | Body height |
| BMI | 39156-5 | Body mass index |