FHIR Programming using Java and HAPI FHIR Server - Working with Canadian FHIR Profiles

Introduction

Welcome back to my series on FHIR Programming using Java and HAPI FHIR Server. In this article, we will explore FHIR profiles and demonstrate their practical application using Canadian Core profiles. Profiles are one of FHIR's most powerful features, allowing organizations and jurisdictions to customize the base FHIR specification to meet their specific requirements while maintaining interoperability.

This tutorial builds upon our previous discussions, including our exploration of building SMART on FHIR applications. If you're unfamiliar with creating and validating resources, I recommend reviewing the earlier articles in this series before proceeding. By the end of this article, you'll understand what FHIR profiles are and how to create profile-compliant resources for Canadian healthcare systems.

What Are FHIR Profiles?

A FHIR profile is a set of constraints and extensions applied to a base FHIR resource. Think of the base FHIR specification as a flexible template that covers the broadest possible use cases. Profiles narrow down this template to meet specific needs while maintaining interoperability.

Profiles can:

  • Mark optional elements as required (cardinality constraints)
  • Restrict the allowed values for coded elements (value set bindings)
  • Add extensions for data elements not in the base specification
  • Remove elements that aren't needed for a particular use case
  • Provide additional documentation and guidance

Profiles are formally defined using StructureDefinition resources and are identified by a canonical URL. When a resource claims conformance to a profile, it includes that profile's URL in the meta.profile element.

Understanding Profile Hierarchies and Implementation Guides

Profiles exist within a hierarchy, with each level adding more specific constraints while maintaining compatibility with the levels above.

Profile Hierarchy

Canadian FHIR implementations typically follow this hierarchy:

Canadian FHIR Profile Hierarchy saravanansubramanian.com each layer tightens constraints while remaining conformant to the layers above Base FHIR R4 Specification broadest definition · maximum flexibility International (IPS, etc.) optional layer for cross-border interoperability CA Core Profiles Canada-wide constraints and identifier systems Provincial Profiles (e.g., ON, BC) province-specific requirements and extensions Organization-Specific Profiles hospital or health-authority specific

Each level inherits all constraints from above and may add additional ones. A resource that conforms to a provincial profile automatically conforms to CA Core and base FHIR.

Extensions: Adding Canadian-Specific Data

FHIR extensions allow adding data elements not defined in the base specification. Extensions are critical for Canadian implementations:

Standard Extensions - Defined by HL7 and widely adopted:

  • Part of the FHIR specification or published implementation guides
  • Have well-defined semantics and canonical URLs
  • Example: http://hl7.org/fhir/StructureDefinition/patient-birthPlace

Canadian Extensions - Defined by Canada Health Infoway:

  • Address Canadian-specific requirements
  • Published as part of CA Core
  • Example extensions might include Indigenous status, preferred pharmacy, etc.

Custom/Local Extensions - Defined by organizations:

  • Should be registered to avoid conflicts
  • Must have a StructureDefinition
  • Should be documented in local implementation guides
// Example: Adding an extension to a Patient
Extension birthPlaceExt = new Extension();
birthPlaceExt.setUrl("http://hl7.org/fhir/StructureDefinition/patient-birthPlace");
birthPlaceExt.setValue(new Address()
    .setCity("Ottawa")
    .setState("ON")
    .setCountry("CA"));
patient.addExtension(birthPlaceExt);

Canadian Terminology Bindings

Profiles specify which code systems and value sets must be used. Canadian implementations use specific terminology:

  • SNOMED CT Canadian Edition - Clinical terminology with Canadian-specific content
  • Canadian Drug Dictionary (CCDD) - Medications used in Canada
  • pCLOCD - pan-Canadian LOINC Observation Code Database for lab results
  • Canadian Clinical Drug Data Set - Drug product information
  • PIPEDA Codes - Privacy and consent-related codes

Using the correct Canadian terminology ensures your data is meaningful and interoperable within the Canadian healthcare system.

Canada Health Infoway’s Role

Canada Health Infoway is the key organization driving FHIR adoption in Canada:

  • Standards Development - Develops and publishes CA Core profiles
  • Identifier Registries - Manages naming systems for provincial health numbers and other identifiers
  • Implementation Guidance - Provides resources for implementing FHIR in Canada
  • Conformance Testing - Offers tools and services for validating FHIR implementations
  • Interoperability Programs - Coordinates pan-Canadian interoperability initiatives

The official CA Core Implementation Guide is available at: https://build.fhir.org/ig/HL7-Canada/ca-core/

Implementation Guides Structure

Implementation Guides (IGs) are comprehensive packages that include:

  • StructureDefinitions - The profiles themselves (constraints on resources)
  • ValueSets - Collections of codes that are valid for specific elements
  • CodeSystems - Definitions of code systems (if custom)
  • NamingSystem - Definitions of identifier systems (like provincial health numbers)
  • SearchParameter - Custom search parameters
  • CapabilityStatement - Expected server capabilities
  • Examples - Sample resources showing proper usage
  • Narrative Documentation - Human-readable guidance

When implementing against CA Core, you should download and include the IG package in your validation pipeline to ensure full conformance.

Prerequisites

Before you start, ensure your environment is ready:

  • Java Development Kit (JDK) is installed and configured.
  • Apache Maven is installed and set up for your project.
  • HAPI FHIR Server is running and accessible.
  • You can find all the code demonstrated in this tutorial on GitHub here

“The aim of argument, or of discussion, should not be victory, but progress.” ~ Joseph Joubert

Canadian Core FHIR Profiles

Canada Health Infoway has developed the CA Core (Canadian Core) FHIR profiles to support healthcare interoperability across Canada. These profiles provide Canadian-specific constraints and extensions on base FHIR resources, ensuring consistency across provincial healthcare systems.

Key aspects of Canadian FHIR profiles include:

  • Provincial Health Numbers - Each province issues unique health identifiers with specific formats and naming systems
  • Canadian Postal Codes - Address validation following the A1A 1A1 format
  • Province/Territory Codes - Standard two-letter codes (ON, BC, QC, AB, SK, etc.)
  • Bilingual Support - Communication preferences for English and French
  • Professional Licensing - Provincial medical licensing identifiers

Provincial Health Number Systems

Canada's healthcare system is provincially administered, meaning each province issues its own health identifiers. Here are the naming system URIs for major provinces:

ProvinceHealth Number NameNaming System URIFormat
OntarioOHIPca-on-patient-hcn10 digits
British ColumbiaPHNca-bc-patient-healthcare-id10 digits (starts with 9)
QuebecNAMca-qc-patient-healthcare-id4 letters + 8 digits
AlbertaPHNca-ab-patient-healthcare-id9 digits
SaskatchewanHSNca-sk-patient-healthcare-id9 digits

Step 1 of 5: Import Required Classes and Define Constants

First, let's set up our imports and define the constants for Canadian profile URLs and identifier systems. This ensures consistency throughout your application.

package com.saravanansubramanian.fhir;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r4.model.*;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class FhirCanadianProfiles {

    // Canadian identifier system URLs (from Canada Health Infoway)
    private static final String ONTARIO_HEALTH_NUMBER_SYSTEM =
        "https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-patient-hcn";
    private static final String BRITISH_COLUMBIA_PHN_SYSTEM =
        "https://fhir.infoway-inforoute.ca/NamingSystem/ca-bc-patient-healthcare-id";
    private static final String ALBERTA_PHN_SYSTEM =
        "https://fhir.infoway-inforoute.ca/NamingSystem/ca-ab-patient-healthcare-id";
    private static final String QUEBEC_NAM_SYSTEM =
        "https://fhir.infoway-inforoute.ca/NamingSystem/ca-qc-patient-healthcare-id";
    private static final String SASKATCHEWAN_PHN_SYSTEM =
        "https://fhir.infoway-inforoute.ca/NamingSystem/ca-sk-patient-healthcare-id";

    // Canadian profile URLs
    private static final String CA_CORE_PATIENT_PROFILE =
        "http://hl7.org/fhir/ca/core/StructureDefinition/profile-patient";
    private static final String CA_CORE_PRACTITIONER_PROFILE =
        "http://hl7.org/fhir/ca/core/StructureDefinition/profile-practitioner";
    private static final String CA_CORE_ORGANIZATION_PROFILE =
        "http://hl7.org/fhir/ca/core/StructureDefinition/profile-organization";

These constants define the official naming systems published by Canada Health Infoway. Using the correct system URI is essential for interoperability, as it allows other systems to recognize and properly interpret the identifiers.

“In the middle of difficulty lies opportunity.” ~ Albert Einstein

Step 2 of 5: Create a CA Core Patient with Ontario Health Number

Now let's create a Patient resource that conforms to the CA Core Patient profile. The key elements include setting the profile in metadata, using the correct provincial health number system, and following Canadian address conventions.

private static Patient createOntarioPatient() {
    Patient patient = new Patient();

    // Set CA Core Patient profile - this declares conformance to the profile
    patient.getMeta().addProfile(CA_CORE_PATIENT_PROFILE);

    // Add Ontario Health Number (OHIP) - Format: 10 digits
    patient.addIdentifier()
            .setSystem(ONTARIO_HEALTH_NUMBER_SYSTEM)
            .setValue("1234567890")
            .setType(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
                            .setCode("JHN")
                            .setDisplay("Jurisdictional health number")))
            .setUse(Identifier.IdentifierUse.OFFICIAL)
            .setPeriod(new Period()
                    .setStart(parseDate("2020-01-01")));

    // Add a secondary identifier (Medical Record Number)
    patient.addIdentifier()
            .setSystem("http://example.org/hospital/mrn")
            .setValue("MRN-ON-12345")
            .setType(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
                            .setCode("MR")
                            .setDisplay("Medical record number")))
            .setUse(Identifier.IdentifierUse.USUAL);

    // Add patient name
    patient.addName()
            .setUse(HumanName.NameUse.OFFICIAL)
            .setFamily("Tremblay")
            .addGiven("Marie")
            .addGiven("Claire")
            .addPrefix("Ms.");

    // Set demographics
    patient.setGender(Enumerations.AdministrativeGender.FEMALE);
    patient.setBirthDate(parseDate("1985-03-15"));
    patient.setActive(true);

    // Add Canadian address with proper formatting
    patient.addAddress()
            .setUse(Address.AddressUse.HOME)
            .setType(Address.AddressType.PHYSICAL)
            .addLine("123 Yonge Street")
            .addLine("Unit 456")
            .setCity("Toronto")
            .setState("ON")  // Canadian province code
            .setPostalCode("M5B 1M4")  // Canadian postal code format (A1A 1A1)
            .setCountry("CA");  // ISO 3166 country code

    // Add contact information
    patient.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.PHONE)
            .setValue("+1-416-555-0123")
            .setUse(ContactPoint.ContactPointUse.HOME);

    patient.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.EMAIL)
            .setValue("[email protected]")
            .setUse(ContactPoint.ContactPointUse.HOME);

    // Add bilingual language support (English and French)
    patient.addCommunication()
            .setLanguage(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("urn:ietf:bcp:47")
                            .setCode("en-CA")
                            .setDisplay("English (Canada)")))
            .setPreferred(true);

    patient.addCommunication()
            .setLanguage(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("urn:ietf:bcp:47")
                            .setCode("fr-CA")
                            .setDisplay("French (Canada)")));

    return patient;
}

// Helper method to parse date strings
private static Date parseDate(String dateString) {
    LocalDate localDate = LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
    return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
}

Notice how the profile URL is set in the meta.profile element using the fluent API. This declares that the resource conforms to the CA Core Patient profile. The identifier type code "JHN" (Jurisdictional health number) indicates this is a government-issued health identifier.

Step 3 of 5: Create Patients for Different Provinces

Each Canadian province has its own health number format and naming conventions. Let's create patients for Quebec and British Columbia to demonstrate the differences.

private static Patient createQuebecPatient() {
    Patient patient = new Patient();

    // Set CA Core Patient profile
    patient.getMeta().addProfile(CA_CORE_PATIENT_PROFILE);

    // Add Quebec NAM - Format: 4 letters + 8 digits (e.g., DUBO75010112)
    // The format encodes: first 4 letters of surname + birth date info
    patient.addIdentifier()
            .setSystem(QUEBEC_NAM_SYSTEM)
            .setValue("DUBO75010112")
            .setType(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
                            .setCode("JHN")
                            .setDisplay("Jurisdictional health number")))
            .setUse(Identifier.IdentifierUse.OFFICIAL);

    // Add patient name (French name)
    patient.addName()
            .setUse(HumanName.NameUse.OFFICIAL)
            .setFamily("Dubois")
            .addGiven("Jean-Pierre")
            .addPrefix("M.");  // French honorific

    patient.setGender(Enumerations.AdministrativeGender.MALE);
    patient.setBirthDate(parseDate("1975-01-01"));
    patient.setActive(true);

    // Add Quebec address with French street name
    patient.addAddress()
            .setUse(Address.AddressUse.HOME)
            .setType(Address.AddressType.PHYSICAL)
            .addLine("456 Rue Sainte-Catherine Ouest")
            .setCity("Montreal")
            .setState("QC")  // Quebec province code
            .setPostalCode("H3B 1A2")
            .setCountry("CA");

    patient.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.PHONE)
            .setValue("+1-514-555-0145")
            .setUse(ContactPoint.ContactPointUse.HOME);

    // Quebec patient - French as preferred language
    patient.addCommunication()
            .setLanguage(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("urn:ietf:bcp:47")
                            .setCode("fr-CA")
                            .setDisplay("French (Canada)")))
            .setPreferred(true);

    return patient;
}

private static Patient createBritishColumbiaPatient() {
    Patient patient = new Patient();

    // Set CA Core Patient profile
    patient.getMeta().addProfile(CA_CORE_PATIENT_PROFILE);

    // Add BC Personal Health Number (PHN) - Format: 10 digits starting with 9
    patient.addIdentifier()
            .setSystem(BRITISH_COLUMBIA_PHN_SYSTEM)
            .setValue("9876543210")
            .setType(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
                            .setCode("JHN")
                            .setDisplay("Jurisdictional health number")))
            .setUse(Identifier.IdentifierUse.OFFICIAL);

    // Add patient name
    patient.addName()
            .setUse(HumanName.NameUse.OFFICIAL)
            .setFamily("Wong")
            .addGiven("David")
            .addGiven("Chen");

    patient.setGender(Enumerations.AdministrativeGender.MALE);
    patient.setBirthDate(parseDate("1978-08-22"));
    patient.setActive(true);

    // Add British Columbia address
    patient.addAddress()
            .setUse(Address.AddressUse.HOME)
            .setType(Address.AddressType.PHYSICAL)
            .addLine("789 Granville Street")
            .setCity("Vancouver")
            .setState("BC")  // British Columbia
            .setPostalCode("V6Z 1K3")
            .setCountry("CA");

    patient.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.PHONE)
            .setValue("+1-604-555-0199")
            .setUse(ContactPoint.ContactPointUse.MOBILE);

    // Add multiple language capabilities
    patient.addCommunication()
            .setLanguage(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("urn:ietf:bcp:47")
                            .setCode("en-CA")
                            .setDisplay("English (Canada)")))
            .setPreferred(true);

    // BC has a significant Chinese-speaking population
    patient.addCommunication()
            .setLanguage(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("urn:ietf:bcp:47")
                            .setCode("zh-Hans")
                            .setDisplay("Chinese (Simplified)")));

    return patient;
}

Each province has unique characteristics reflected in the patient data. Quebec uses French names and addresses with "M." as a prefix instead of "Mr.", while British Columbia addresses the multicultural nature of its population with support for multiple languages.

“The secret of change is to focus all of your energy not on fighting the old, but on building the new.” ~ Socrates

Step 4 of 5: Create a CA Core Practitioner

Healthcare practitioners in Canada are licensed by provincial regulatory bodies. The CA Core Practitioner profile includes support for provincial licensing identifiers and professional qualifications.

private static Practitioner createCanadianPractitioner() {
    Practitioner practitioner = new Practitioner();

    // Set CA Core Practitioner profile
    practitioner.getMeta().addProfile(CA_CORE_PRACTITIONER_PROFILE);

    // Add College of Physicians and Surgeons of Ontario (CPSO) license number
    practitioner.addIdentifier()
            .setSystem("https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-license-physician")
            .setValue("12345")
            .setType(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
                            .setCode("MD")
                            .setDisplay("Medical License Number")))
            .setUse(Identifier.IdentifierUse.OFFICIAL)
            .setPeriod(new Period()
                    .setStart(parseDate("2010-06-15")));

    // Add practitioner name with credentials
    practitioner.addName()
            .setUse(HumanName.NameUse.OFFICIAL)
            .setFamily("Singh")
            .addGiven("Priya")
            .addPrefix("Dr.")
            .addSuffix("MD, FRCPC");  // Fellow of Royal College of Physicians

    practitioner.setGender(Enumerations.AdministrativeGender.FEMALE);
    practitioner.setActive(true);

    // Add work address
    practitioner.addAddress()
            .setUse(Address.AddressUse.WORK)
            .setType(Address.AddressType.PHYSICAL)
            .addLine("200 University Avenue")
            .setCity("Toronto")
            .setState("ON")
            .setPostalCode("M5G 1V2")
            .setCountry("CA");

    // Add contact information
    practitioner.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.PHONE)
            .setValue("+1-416-555-0200")
            .setUse(ContactPoint.ContactPointUse.WORK);

    practitioner.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.EMAIL)
            .setValue("[email protected]")
            .setUse(ContactPoint.ContactPointUse.WORK);

    // Add medical qualifications
    practitioner.addQualification()
            .setCode(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://terminology.hl7.org/CodeSystem/v2-0360")
                            .setCode("MD")
                            .setDisplay("Doctor of Medicine")))
            .setIssuer(new Reference().setDisplay("University of Toronto"))
            .setPeriod(new Period()
                    .setStart(parseDate("2008-05-15")));

    // Add specialty qualification (FRCPC - Fellow of Royal College)
    practitioner.addQualification()
            .setCode(new CodeableConcept()
                    .addCoding(new Coding()
                            .setSystem("http://snomed.info/sct")
                            .setCode("394802001")
                            .setDisplay("General medicine")))
            .setIssuer(new Reference()
                    .setDisplay("Royal College of Physicians and Surgeons of Canada"))
            .setPeriod(new Period()
                    .setStart(parseDate("2010-06-15")));

    // Add language capabilities (multilingual practitioner)
    practitioner.addCommunication()
            .addCoding(new Coding()
                    .setSystem("urn:ietf:bcp:47")
                    .setCode("en-CA")
                    .setDisplay("English (Canada)"));

    practitioner.addCommunication()
            .addCoding(new Coding()
                    .setSystem("urn:ietf:bcp:47")
                    .setCode("hi")
                    .setDisplay("Hindi"));

    practitioner.addCommunication()
            .addCoding(new Coding()
                    .setSystem("urn:ietf:bcp:47")
                    .setCode("pa")
                    .setDisplay("Punjabi"));

    return practitioner;
}

The practitioner example demonstrates several Canadian-specific elements: the CPSO (College of Physicians and Surgeons of Ontario) licensing system, the FRCPC credential from the Royal College of Physicians and Surgeons of Canada, and support for multiple languages reflecting Canada's multicultural population.

Step 5 of 5: Create a CA Core Organization and Submit Resources

Healthcare organizations in Canada also follow specific identification patterns. Let's create an organization and then submit our resources to a FHIR server.

private static Organization createCanadianOrganization() {
    Organization organization = new Organization();

    // Set CA Core Organization profile
    organization.getMeta().addProfile(CA_CORE_ORGANIZATION_PROFILE);

    // Add Canadian Organization Identifier from Infoway
    organization.addIdentifier()
            .setSystem("https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-organization-id")
            .setValue("12345678")
            .setUse(Identifier.IdentifierUse.OFFICIAL);

    // Add organization name
    organization.setName("Toronto General Hospital");

    // Add aliases (alternative names)
    organization.addAlias("TGH");
    organization.addAlias("University Health Network - Toronto General");

    // Set organization type
    organization.addType()
            .addCoding(new Coding()
                    .setSystem("http://terminology.hl7.org/CodeSystem/organization-type")
                    .setCode("prov")
                    .setDisplay("Healthcare Provider"));

    organization.addType()
            .addCoding(new Coding()
                    .setSystem("http://terminology.hl7.org/CodeSystem/organization-type")
                    .setCode("team")
                    .setDisplay("Organizational Team"))
            .setText("Teaching Hospital");

    organization.setActive(true);

    // Add address
    organization.addAddress()
            .setUse(Address.AddressUse.WORK)
            .setType(Address.AddressType.PHYSICAL)
            .addLine("200 Elizabeth Street")
            .setCity("Toronto")
            .setState("ON")
            .setPostalCode("M5G 2C4")
            .setCountry("CA");

    // Add contact information
    organization.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.PHONE)
            .setValue("+1-416-340-4800")
            .setUse(ContactPoint.ContactPointUse.WORK);

    organization.addTelecom()
            .setSystem(ContactPoint.ContactPointSystem.URL)
            .setValue("https://www.uhn.ca/TorontoGeneral")
            .setUse(ContactPoint.ContactPointUse.WORK);

    return organization;
}

public static void main(String[] args) {
    String fhirServerUrl = "http://hapi.fhir.org/baseR4";

    System.out.println("FHIR Java SDK - Canadian FHIR Profiles Tutorial");
    System.out.println("================================================");

    try {
        // Initialize FHIR context for R4
        FhirContext ctx = FhirContext.forR4();
        IGenericClient client = ctx.newRestfulGenericClient(fhirServerUrl);

        // Create Canadian patients
        Patient ontarioPatient = createOntarioPatient();
        Patient quebecPatient = createQuebecPatient();
        Patient bcPatient = createBritishColumbiaPatient();

        // Create practitioner and organization
        Practitioner practitioner = createCanadianPractitioner();
        Organization organization = createCanadianOrganization();

        // Validate resources locally
        System.out.println("\nValidating Canadian Resources...");
        ValidationSupportChain validationSupportChain = new ValidationSupportChain(
                new DefaultProfileValidationSupport(ctx),
                new InMemoryTerminologyServerValidationSupport(ctx),
                new CommonCodeSystemsTerminologyService(ctx)
        );

        FhirValidator validator = ctx.newValidator();
        FhirInstanceValidator instanceValidator = new FhirInstanceValidator(validationSupportChain);
        validator.registerValidatorModule(instanceValidator);

        ValidationResult result = validator.validateWithResult(ontarioPatient);
        System.out.println("Ontario Patient validation: " + (result.isSuccessful() ? "PASSED" : "FAILED"));

        // Submit resources to FHIR server
        System.out.println("\nSubmitting resources to FHIR server...");

        MethodOutcome outcome = client.create().resource(ontarioPatient).execute();
        if (outcome.getId() != null) {
            System.out.println("Ontario Patient created with ID: " + outcome.getId().getIdPart());
        }

        // Search for patients by provincial health number
        System.out.println("\nSearching for patients with Ontario health numbers...");
        Bundle searchResults = client.search()
                .forResource(Patient.class)
                .where(Patient.IDENTIFIER.hasSystemWithAnyCode(ONTARIO_HEALTH_NUMBER_SYSTEM))
                .returnBundle(Bundle.class)
                .execute();

        System.out.println("Found " + searchResults.getTotal() + " patients.");

    } catch (Exception e) {
        System.err.println("Error: " + e.getMessage());
        e.printStackTrace();
    }

    System.out.println("\nTutorial completed.");
}

Canadian FHIR Profile Best Practices

When working with Canadian FHIR profiles, follow these best practices to ensure interoperability and compliance:

  • Always use the appropriate provincial health number system URI - Each province has its own naming system URL published by Infoway
  • Use Canadian postal code format (A1A 1A1) - Include the space between the two halves
  • Reference CA Core profile URLs in meta.profile - This declares your resource's conformance to the profile
  • Use standard Canadian province/territory codes - Use two-letter codes: ON, BC, QC, AB, SK, MB, NS, NB, PE, NL, NT, YT, NU
  • Include language preferences - Support for both English (en-CA) and French (fr-CA) is important for Canadian healthcare
  • Follow Infoway's pan-Canadian standards - Consult the official CA Core implementation guide for detailed requirements
  • Validate resources against CA Core profiles before production use - Use the validation techniques covered in earlier tutorials

Conclusion

In this article, we've explored FHIR profiles using Canadian Core profiles as practical examples. You've learned what profiles are and how they constrain base FHIR resources, how Canadian Core profiles support provincial health systems, how to create profile-compliant resources for patients, practitioners, and organizations, and best practices for working with Canadian FHIR implementations.

Profiles are essential for real-world FHIR implementations because they bridge the gap between FHIR's flexibility and the specific requirements of healthcare jurisdictions. By understanding how to work with profiles like CA Core, you're well-equipped to build interoperable healthcare applications that work within Canada's healthcare ecosystem.

The complete code for this tutorial is available on GitHub. For more information on Canadian FHIR profiles, visit the CA Core Implementation Guide.

This concludes the FHIR programming series using Java. Throughout this series, we've covered everything from setting up your environment, through CRUD operations, searching, validation, chaining operations, advanced topics including use of profiles in FHIR, security best practices, and finally building a SMART on FHIR application. You now have a comprehensive foundation for building robust, secure, and interoperable healthcare applications using FHIR and Java. Thank you for following along!