Thursday, October 10, 2024

Public Sector Day Singapore 2024


I was delighted to be invited by AWS to share how we designed and implemented robust tech foundation for Singapore Healthier SG and Age Well SG at recent Public Sector Day Singapore 2024 on 03 Oct 202

It is hard to design robust future ready tech foundation for such large scale solution with extremely high throughout and fast performance, in generally all the APIs are performing at around 0.2 to 0.3 second even at peak volume. It is also relatively easy if the team has been consciously investing in the following four areas

  1. Deeper understanding of business domains, ability to shape the requirements, and ability to predict what will come next
  2. Deeper understanding of technologies - products/solutions and industry trend, set future ready technology direction, be ready to transform your team, the organisation and the industry
  3. Once you are equipped with deep understanding of business and technology, you will be naturally have the courage and determination to hold the vision and direction
  4. Build strong team whom you can count on so that you are confident that things will work out well even though some parts are not clear or fully sorted out yet at beginning
The two pillars of our strong tech foundation are AWS cloud platform and HL7 FHIR
  1. AWS has the broadest and richest cloud services for us to build modern cloud native and highly resilient applications
    • Use API GW, AWS EKS, RDS,SQS, Lambda and ActiveMQ for transactional part of the application
    • Use AWS Glue, AWS Data Lake for Operational Data Store 
    • Use AWS BedRock for RAG implementation
  2. HL7 FHIR provides suite of framework for us to build future proof applications
    • Use HL7 Extended Operation to define fined grained APIs to be accessed by external systems
    • Use HL7 FHIR data model as basis for our internal data model, so that it can support any future business needs without change to the data model
    • HL7 FHIR Profile as data validation control for API, any ongoing changes are handled with simple update to the profile without single line of code at all. For example, if we need to support different set of goals and activities for Health Plan, we can just update the profile for Health Plan API
    • Use "SQL On FHIR" to have universal transformation to transform any data subject to any data format required by data analysis and visualisation tool


Below are some photos taken during the public sector day  and the slides (in MP4 format at the bottom of this post)

Enjoy!
















Wednesday, September 25, 2024

HL7 FHIR difficult to implement - Misperception or truth?

Recently I am approached by one project team, who stated that it is very difficult to implement HL7 FHIR API with the following two reasons

1) Their system only supports RESTful with JSON

2) They are not familiar with HL7 FHIR

For technical people who has some basic understanding of HL7 FHIR, you must be laughing,  since HL7 FHIR API is using RESTful with JSON, and only difference between HL7 FHIR and other proprietary API is that the format and syntax in HL7 FHIR is standardised

Instead of brushing it aside, and knowing that the system is using Java. So I spent 5 minutes to ask Chatgrp, how to implement client side programme in Java to call HL7 FHIR API

Afterward I spent next few mins to try out and validate in Java IDE (Integrated Development Environment) Eclipse.

Below I briefly described the steps provided by chatgrp
1) Create a Maven project in Eclipse
2) Configure the dependent libraries(HAPI FHIR libraries) in Maven POM
3) Copy and paste the sample code provided by chatgpt into my Java application, and updated the remote FHIR server to FHIR server hosted by Firely (its FHIR server is written in .NET)
Below is the source code of the java application, you can run the application to create patient resource, once it is created, you can check the created resources at Firely Server using this URL - https://server.fire.ly/

package synapxe.fhir.demo;

import java.util.Calendar;

import java.util.Random;

import org.apache.commons.lang3.RandomStringUtils;

import org.hl7.fhir.r4b.model.Bundle;

import org.hl7.fhir.r4b.model.Enumerations;

import org.hl7.fhir.r4b.model.Identifier;

import org.hl7.fhir.r4b.model.Patient;

import ca.uhn.fhir.context.FhirContext;

import ca.uhn.fhir.rest.api.MethodOutcome;

import ca.uhn.fhir.rest.client.api.IGenericClient;

/**

* Sample FHIR Client using HAPI open source library https://github.com/hapifhir/hapi-fhir

*

* @author victorchai

*

*/

public class FHIRClient {

public static void main(String[] args) {

try {

createPatient ();

//searchPatientByName ("HapiVictor");

//deletePatient("cfc18bb8-e1dc-4bab-b4e2-83e1d797034f");

}

catch (Exception ex) {

ex.printStackTrace();

}

}

private static void createPatient() {

// Create a FHIR context for the FHIR version you are using (e.g., R4)

FhirContext ctx = FhirContext.forR4B();

// Create a client to connect to the FHIR server

String serverBaseUrl = "https://server.fire.ly"; // Firely FHIR Server which is implemented in .NET

IGenericClient client = ctx.newRestfulGenericClient(serverBaseUrl);

// Create a Patient resource

Patient patient = new Patient();

Identifier nric = new Identifier();

nric.setSystem("http://fhir.synapxe.sg/Namespace/nric");

String nricDigits = RandomStringUtils.randomNumeric(7).toUpperCase();

nric.setValue("S" +nricDigits + "H");

patient.getIdentifier().add(nric);

patient.addName()

.setFamily("HapiVictor")

.addGiven("John");

patient.setGender(Enumerations.AdministrativeGender.MALE);

Calendar dob= Calendar.getInstance();

Random ran = new Random();

int x = ran.nextInt(10);

dob.set(1970 +x ,Calendar.getInstance().get(Calendar.MONTH), Calendar.getInstance().get(Calendar.DAY_OF_MONTH));

patient.setBirthDate(dob.getTime());

// Send the created patient resource to the server

MethodOutcome outcome = client.create().resource(patient).execute();

// Print the outcome

String patientID = outcome.getId().getIdPart();

System.out.println("Created Patient with ID: " + patientID);

System.out.println("You can access the patient resource at this url - "

+ serverBaseUrl +"/Patient/"+ patientID +"?_format=application/fhir+json");

}

private static void searchPatientByName (String familyName ) {

// Create a FHIR context for the FHIR version you are using (e.g., R4)

FhirContext ctx = FhirContext.forR4B();

// Create a client to connect to the FHIR server

String serverBaseUrl = "https://server.fire.ly"; // Example HAPI FHIR server

IGenericClient client = ctx.newRestfulGenericClient(serverBaseUrl);

Bundle results = client.search()

.forResource(Patient.class)

.where(Patient.FAMILY.matches().value(familyName))

.returnBundle(Bundle.class)

.execute();

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

for (Bundle.BundleEntryComponent entry : results.getEntry()) {

Patient patient = (Patient) entry.getResource();

System.out.println("Patient ID:" + patient.getIdElement().getIdPart());

System.out.println("Patient Name:" + patient.getNameFirstRep().getNameAsSingleString());

}

}

private static void deletePatient(String patientID) {

// Create a FHIR context for the FHIR version you are using (e.g., R4)

FhirContext ctx = FhirContext.forR4B();

// Create a client to connect to the FHIR server

String serverBaseUrl = "https://server.fire.ly"; // Example HAPI FHIR server

IGenericClient client = ctx.newRestfulGenericClient(serverBaseUrl);

// Send the patient resource ID to the server

MethodOutcome outcome = client.delete().resourceById("Patient", patientID).execute();

// Print the outcome

System.out.println("Deleted Patient with ID: " + patientID + " with status:" + outcome.getResponseStatusCode()) ;

}

}

Below you can also find the libraries included in  Maven POM.xml which specifies the dependent libraries (again this is also provided by Chatgpt)

<dependencies>

<!-- This dependency includes the core HAPI-FHIR classes -->

<dependency>

<groupId>ca.uhn.hapi.fhir</groupId>

<artifactId>hapi-fhir-base</artifactId>

<version>${hapifhir_version}</version>

</dependency>

<!-- Include the client -->

<dependency>

<groupId>ca.uhn.hapi.fhir</groupId>

<artifactId>hapi-fhir-client</artifactId>

<version>${hapifhir_version}</version>

</dependency>

<!-- At least one "structures" JAR must also be included -->

<dependency>

<groupId>ca.uhn.hapi.fhir</groupId>

<artifactId>hapi-fhir-structures-r4b</artifactId>

<version>${hapifhir_version}</version>

</dependency>

<!--

HAPI-FHIR uses Logback for logging support. The logback library is included

automatically by Maven as a part of the hapi-fhir-base dependency, but you

also need to include a logging library. Logback is used here, but log4j

would also be fine.

-->

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.2.3</version>

</dependency>

</dependencies>


Btw, there are many many HL7 FHIR open sources libraries available - Java, Net, Python and Javascript, etc, you can find the full list at this URL - https://confluence.hl7.org/.../Open+Source+Implementations
Go explore HL7 FHIR and have fun. Pls msg me if you need have deeper discussion on HL7 FHIR


Tuesday, July 2, 2024

HL7 FHIR DevDays 2024 - Sharing of Singapore's Healthier SG implementation

 Below is the some of the slides from the recent sharing at FHIR DevDays 2024 (https://www.devdays.com/) on Singapore's National Healthier SG implementation.









I am also glad that the implementation got the inaugural FHIR implementers award, below is the finalist pitch I shared during DevDays







For more details, please go to the following Firely site on the highlights of FHIR DevDays 2024



Friday, August 11, 2023

How to model Patient flowsheet in HL7 FHIR

1. Overview

Recently I am working on the HL7 FHIR information model to represent patient flowsheet for the purpose of integration with one EMR vendor product, the EMR vendor also does not have information and model to represent patient flowsheet, though the reading is represented using Observation resource.

I searched FHIR chat group, there is also no specific implementation guideline on patient flowsheet, thus I have proposed the following design, I welcome comment and discussion on this topic.

1) Use PlanDefinition resource to represent the flowsheet

2) Use CarePlan resource to represent the association of the selected flowsheet to the patient

3) For the Observation resource, use "basedOn" attribute to specify the specific patient flowsheet for the reading submission


Below diagram illustrates the model and relationship between the three resources




2. CarePlan resource

CarePlan resource to represent the specific flowsheet selected for the patient, the "category" attribute will be used to specify this care plan is patient flowsheet.



3. PlanDefinition resource for the flowsheet which can be selected and assigned to the patient





4. Observation resource for the patient flowsheet reading, use "basedOn" to associate wth the patient flowsheet



5. Revised Design after consultation within FHIR community

After discussion within FHIR community, the consensus is that we should use Questionnaire to represent the Flowsheet template.

Below please find the overview of the revised model (Click on the image to Zoom In)




Thursday, May 18, 2023

Leverage HL7 FHIR Operation to achieve the purpose of "Small Functional APIs"

Overview

During the course of design and implementation for API for modern API driven development, various engineers have been asking the gradually of API, how much responsibility each API shall have, and how to balance the initial implementation and ongoing maintenance of the APIs from both API provider and API consumer perspectives. In this post, I will be sharing my recommendations from my personal professional perspective, do let me know your view.

Technology has been evolving, eg from SOAP to RESTful, from monolithic application design to SOA and now MSA, however the underlying design principles remains unchanged, and we can still go back to the decades old design principles to guide modern application design, and API design specifically in this post.

One of the design principles I would like to reference here is the following para from "Basics of the Unix Philosophy"

"Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new features."

As this quote conveys, the Unix culture centers around small and simple tools that can be combined. This advice still rings true even after more than 45 years later when we deal with API design and development. To adapt this principle to API design, we may say,  "Make each API do one thing well".

Benefits of making small functional APIs

So what's the benefits of adopting this API design principle, there are three benefits according to this books - Building Secure and Reliable Systems

  1. Design for Understandability. 
  2. Design for Least Privilege 
  3. Design for Resilience 


Design for Understandability

It is much easier for API consumer to understand the API as it is small functional API, it has very specific functional responsibility, it has very specific input parameters and expected response. It is also easier to API provider to rollout new API faster and safer since it does not involve the changing of existing gigantic API

Design for Least Privilege

Each API can be enforced specifically to allow certain API consumers to call, this can be easily enforced at the typical API Gateway products. In contrast to very coarse grained API, this can not done, as API gateway will not be able to go into the API payload to check whether a certain API consumer is allowed to call or not, also if the API gateway starting to go down to this level of detail, then it also embeds the business specific logics, it will complicate the API gateway management and maintenance

Design for Resilience

Wth each API only serve for specific functional responsibility, we can also control the blast radius. When things go wrong, only the specific APIs are affected, thus only specific functional areas are affected.


How to design small functional APIs


However "great power comes with great responsibility", to reap the benefits, we need to take extra care to have good design in place, to ensure the API design is consistent and scalable across many different small functional APIs.

Luckily we do not need to start from scratch, HL7 FHIR also defined capability for standardised, consistent and scalable way of designing small functional APIs, this is the Extended Operations on RESTful APIs.


I will cover the use of HL7 FHIR Operations capability in next post, stay tuned!

Thursday, August 11, 2022

Use HL7 FHIR API to achieve greater productivity for frontend application development

HL7 FHIR API is modern RESTful API with JSON data structure, it not only defines the payload format, it also defines the API syntax eg HTTP method, URL path and query parameters. It has been adopted by EMR vendors to promote interoperability between different healthcare systems.
The original intent of HL7 FHIR when it was conceptualised back in May 2010 HL7 WGM is to create next generation light-weight interoperability standards to replace the previous SOAP/XML based integration standards, however gradually the industry recognised that this lightweight RESTful/JSON based API standards also fits extremely well for internal backend APIs to be called by frontend Web Applications such as ReactJS based SPA (Single Page Application)
In today's post, I would like to share how HL7 FHIR API dramatically simplifies the development for ReactJS SPA and increase development productivity,
  1. Create a reusable component for backend API integration
  2. How the UI component will call the reusable component to retrieve the data
I am going to share with you a demo ReactJS application to demonstrate how easy it is to achieve the above two objectives.
The demo ReactJS application is hosted at the following site - https://muqhwj.csb.app
Pls take note that the intent of the demo app is NOT to demonstrate the good design practice of ReactJS SPA, eg I did not use TypeScript in the demo yet, the configuration is not maintained in external configuration settings etc. I only spent 1 or 2 days to put up the demo ReactJS SPA.
The demo application shows whatever health records available in the backend system with a simple UI design - summary view and detail view. There is only one API component and one UI component 
  1. All the backend API integration is coded in this API slice - https://codesandbox.io/s/muqhwj.... 
    1. I use the method - getResourcesEntries for getting ALL kinds of resources (other than Patient record) and also do transformation from backend process API to experience API for the Summary View (I moved the included patient resources into respective patient data resource eg Condition under the element "_includedPatient", so that it is easier for UI to render the data). 
    2. The second method is getResourceDetail which is to get the detail payload of the specific record for any given type of resource for the Detail View
  2. The UI layout is implemented in these two files
If you want to try out yourself, you can fork to a new project, and then try to modify these files to add additional record type to be shown in UI, or update the data elements to be shown for each record type
  1. Add new record type and corresponding menu item name in this file - https://codesandbox.io/s/muqhwj?file=/src/app/Navbar.js
  2. Add/update data elements to be shown for the corresponding record type - update function resourceSummaryElements in this file - https://codesandbox.io/s/muqhwj...
  3. Also you need to point your own FHIR server, please modify the server setting in the fhirAPISlice.js - https://codesandbox.io/s/muqhwj?file=/src/api/fhirApiSlice.js

Happy coding and have fun! 

Monday, May 23, 2022

Automated API implementation and documentation

In the traditional development approach, development team generally follows the below 3 steps to design API and maintain API documentation

  1. Design and implement API
  2. Generate Swagger file for API spec
  3. Develop and maintain API documentation in Word document, include the generated Swagger file, and list out the APIs and corresponding request & response payload structure for ease of readability

One most frequently occurring issue of the above process is that the API documentation in step 3 tends to be out of date after the initial application release. At beginning the team will diligently spend time to develop the documentation, however after a while, due to tight project schedule and developers lacking time, the documentation will be out of sync with the actual APIs since every minor change may involve manual updating of the documentation.

When I worked on the recent project for implementing next generation healthcare digital platform, we adopted HL7 FHIR for the API design, we also make use of HL7 FHIR CapabiltyStatement resource to have automated way of designing API and keeping API documentation always in sync with the actual code running in the system

  1. Use CapabilityStatement to define the API and request & response payload structure based on requirements
  2. The CapabilityStatement will be part of the application source code. During CI/CD, the build/release pipeline will generate the corresponding Swagger file from the CapabilityStatement resource, and publish to API documentation web site
  3. The API documentation web site loads and renders the newly designed APIs using RapiDoc.
  4. The newly designed API will be automatically made available when the application is loaded with the updated CapabilityStatement


Below figure describes the approach we adopted to achieve the following two benefits

  1. The API implemented is always in sync with the API designed
  2. The API documentation is always in sync with the API implemented and running in the system.



Public Sector Day Singapore 2024

I was delighted to be invited by AWS to share how we designed and implemented robust tech foundation for Singapore Healthier SG and Age Well...