Skip to content

Tutorial 3: Patient DTO & Mapper

Pages: 2


📖 Page 1: Patient DTO

What is DTO?

DTO = Data Transfer Object = Simple Java object for moving data

Why? Since FHIR resources are complex with many nested fields, we use DTOs to extract only the data we need for our UI or API responses

Create PatientDTO

Create src/main/java/com/healthcare/pms/dto/PatientDTO.java:

package com.healthcare.pms.dto;

import jakarta.validation.constraints.*;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;

@Data
public class PatientDTO {
    private String id;

    @NotBlank(message = "First name required")
    private String firstName;

    @NotBlank(message = "Last name required")
    private String lastName;

    @NotNull @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthDate;

    @NotBlank
    private String gender;

    @Email
    private String email;

    private String phone;
    private String addressLine;
    private String city;
    private String state;
    private String postalCode;
    private String country;
    private String bloodGroup;
    private Boolean active = true;

    public String getFullName() {
        return firstName + " " + lastName;
    }
}

That's it! Simple patient data with validation.


📖 Page 2: Patient Mapper

What is Mapper?

Mapper = Translator between DTO ↔ FHIR

Create PatientMapper

Create src/main/java/com/healthcare/pms/mapper/PatientMapper.java:

package com.healthcare.pms.mapper;

import com.healthcare.pms.dto.PatientDTO;
import org.hl7.fhir.r4.model.*;
import org.springframework.stereotype.Component;
import java.time.ZoneId;
import java.util.Date;

@Component
public class PatientMapper {

    // DTO → FHIR
    public Patient toFhirResource(PatientDTO dto) {
        Patient patient = new Patient();

        if (dto.getId() != null) patient.setId(dto.getId());

        // Name
        HumanName name = new HumanName();
        name.setFamily(dto.getLastName());
        name.addGiven(dto.getFirstName());
        patient.addName(name);

        // Gender
        if (dto.getGender() != null) {
            patient.setGender(Enumerations.AdministrativeGender.valueOf(
                dto.getGender().toUpperCase()
            ));
        }

        // Birth Date
        if (dto.getBirthDate() != null) {
            patient.setBirthDate(Date.from(
                dto.getBirthDate().atStartOfDay(ZoneId.systemDefault()).toInstant()
            ));
        }

        // Email
        if (dto.getEmail() != null) {
            ContactPoint email = new ContactPoint();
            email.setSystem(ContactPoint.ContactPointSystem.EMAIL);
            email.setValue(dto.getEmail());
            patient.addTelecom(email);
        }

        // Phone
        if (dto.getPhone() != null) {
            ContactPoint phone = new ContactPoint();
            phone.setSystem(ContactPoint.ContactPointSystem.PHONE);
            phone.setValue(dto.getPhone());
            patient.addTelecom(phone);
        }

        // Address
        if (dto.getAddressLine() != null) {
            Address address = new Address();
            address.addLine(dto.getAddressLine());
            address.setCity(dto.getCity());
            address.setState(dto.getState());
            address.setPostalCode(dto.getPostalCode());
            address.setCountry(dto.getCountry());
            patient.addAddress(address);
        }

        // Blood Group (Extension)
        if (dto.getBloodGroup() != null) {
            Extension ext = new Extension();
            ext.setUrl("http://example.org/blood-group");
            ext.setValue(new StringType(dto.getBloodGroup()));
            patient.addExtension(ext);
        }

        patient.setActive(dto.getActive());
        return patient;
    }

    // FHIR → DTO
    public PatientDTO toDTO(Patient patient) {
        PatientDTO dto = new PatientDTO();

        dto.setId(patient.getIdElement().getIdPart());

        // Name
        if (patient.hasName()) {
            HumanName name = patient.getNameFirstRep();
            dto.setLastName(name.getFamily());
            dto.setFirstName(name.getGivenAsSingleString());
        }

        // Gender
        if (patient.hasGender()) {
            dto.setGender(patient.getGender().toCode());
        }

        // Birth Date
        if (patient.hasBirthDate()) {
            dto.setBirthDate(patient.getBirthDate().toInstant()
                .atZone(ZoneId.systemDefault()).toLocalDate());
        }

        // Telecom
        for (ContactPoint cp : patient.getTelecom()) {
            if (cp.getSystem() == ContactPoint.ContactPointSystem.EMAIL) {
                dto.setEmail(cp.getValue());
            } else if (cp.getSystem() == ContactPoint.ContactPointSystem.PHONE) {
                dto.setPhone(cp.getValue());
            }
        }

        // Address
        if (patient.hasAddress()) {
            Address addr = patient.getAddressFirstRep();
            dto.setAddressLine(addr.getLine().isEmpty() ? null : 
                addr.getLine().get(0).getValue());
            dto.setCity(addr.getCity());
            dto.setState(addr.getState());
            dto.setPostalCode(addr.getPostalCode());
            dto.setCountry(addr.getCountry());
        }

        // Blood Group
        for (Extension ext : patient.getExtension()) {
            if (ext.getUrl().contains("blood-group")) {
                dto.setBloodGroup(ext.getValue().toString());
            }
        }

        dto.setActive(patient.getActive());
        return dto;
    }
}

✅ You Now Have

  • PatientDTO with all fields and validation
  • PatientMapper for DTO ↔ FHIR conversion
  • Two-way conversion supported: FHIR to DTO and DTO back to FHIR

🚀 Next

Data structures ready! Now let's create CRUD operations.

→ Tutorial 4: Patient CRUD Operations


💡 Quick Tips

Lombok Magic

@Data auto-generates getters, setters, toString, equals, hashCode

Validation

@NotBlank, @Email, @Past validate on form submission

Extensions

Use Extensions for custom FHIR fields (like blood group)