Showing posts with label Architecture. Show all posts
Showing posts with label Architecture. Show all posts

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, March 7, 2013

The Purpose of Architecture

Very thought-provoking interview from MIT's Ross

MIT’s Ross on How Enterprise Architecture and IT More Than Ever Lead to Business Transformation


Particurily the following two points,

If you have all the money in the world, you’re not forced to make tough decisions. Architecture is all about making tough decisions, understanding your tradeoffs, and recognizing that you’re going to get some things that you want and you are going to sacrifice others

We really just need architecture to pull out unnecessary cost and to enable desirable reusability. And the architect is typically going to be the person representing that enterprise view and helping everyone understand the benefits of understanding that enterprise view, so that everybody who can easily or more easily see the local view is constantly working with architects to balance those two requirements.



Tuesday, August 28, 2012

The Design of FHIR Prescription Resource

Below is the summary of the recent discussion wrt FHIR Prescription resource, mainly centered on the following design considerations

  • Granularity of resource and transaction scope
  • How should resources be aggregated for batch PUT submission

Pls bear in mind that current Prescription resource design is not final, there will be further discussion during the upcoming HL7 WGM and the Connectathon.

I put up the key points and my personal opinions here to facilitate further discussion if needed.

 1.  Granularity of resources and transaction scope
 In the figure below copied from FHIR v0.0.5, each  Prescription resource contains only one medicine, whereas in both HL7 v3 and IHE pharmacy it is following the common practice - one prescription contain multiple medicines.




Based on FHIR design principles explained in FHIR overview, resources are the smallest unit of operation and a transaction scope of their own, so in Prescription resource design, each prescription contains only one medicine since each medicine can have its own life cycle, resource is kept at medicine level is also easier for REST processing.

For the above design, I have two main concerns

  1. One of my concerns for the above design is that we are forcing implementers to send 'patient' and 'prescriber' repeatedly if a doctor prescribes multiple medications at one go. 
  2. Another concern is that the XML payload becomes bigger due to repeated data elements, and more critically it leads to processing inefficiency and overhead in REST PUT.

After clarification with key designers of FHIR, I came to know that we can potentially use aggregation mechanism to combine multiple Prescription resources if there are multiple medicines prescribed at one go as shown in below pseudo-XML. Yello-color highlighted XML content is repeated if we need to aggregate multiple prescription resources at one go.

<?xml version="1.0" encoding="UTF-8"?>
<CombinedMedicationPrescription>

    <prescriptionId><id>1.2.36.146595217.4.19.1.3141.20121202001</id></prescriptionId>
    <patient>  
        <type>Patient</type> <id>23453</id>
    </patient>
    <prescriber>
        <type>Agent</type> <id>567</id>
   </prescriber>


   <prescriptionReference>    <!-- Reference to first prescription --> </prescriptionReference>
   <prescriptionReference>    <!-- Reference to seond prescription --> </prescriptionReference>

   <prescription>
      <prescriptionItemId><id>1.2.
36.146595217.4.19.1.3141.1.1</id></prescriptionItemId>
      <patient>
            <type>Patient</type> <id>23453</id>
      </patient>
      <prescriber>
            <type>Agent</type> <id>567</id>
      </prescriber>
      <prescribed>2011-03-04T11:45:33+01:00</prescribed>
      <dispense>
          <dispenser>
                <type>Organization</type> <id>784</id>
          </dispenser>
      </dispense>

      <medicine> .... . </medicine>
  </prescription>
   <prescription>
    <prescriptionItemId><id>1.2.
36.146595217.4.19.1.3141.1.2</id></prescriptionItemId>
      <patient>
            <type>Patient</type> <id>23453</id>
      </patient>
      <prescriber>
            <type>Agent</type> <id>567</id>
      </prescriber>
      <prescribed>2011-03-04T11:45:33+01:00</prescribed>
      <dispense>
          <dispenser>
                <type>Organization</type> <id>784</id>
          </dispenser>
      </dispense>

      <medicine> .... . </medicine>
  </prescription>
</
CombinedMedicationPrescription>

Use the above pseudo-XML as an example, however there will number of fine grained HTTP round trip  traffic to process the complete data in the current design where each resource is submitted one by one.

Each medication prescription has <prescriptionItemId> value for tracking individual medication prescribed.  The <CombinedMedicationPrescription> aggregate resource also has <prescriptionId> to identify the prescription number, and contains the references to the two prescription resources. (CombinedMedicationPrescription resource does not existing in current FHIR, this is resource I make up to illustrate the point).

When the system receives the REST PUT from client end, since it can't create the <CombinedMedicationPrescription> resource without the resource reference to the individual prescription. So instead the client needs  to submit multiple HTTP PUT first to create the <prescription> child resource for each medication prescribed,  and then the final HTTP PUT to create the <CombinedMedicationPrescription> aggregate resource with attributes such as prescriptionId and resource references to the previously created <prescription> resource.

So the number of network level transactions involved for prescription is N+1 where N is the number of medicine prescribed.

 
For this issue, it is good to hear that FHIR will consider a batch put after Connectathon. so this solves one part of my concerns.



2. How should resources be aggregated for Batch PUT submission

Batch PUT solves one part of the problems, it reduces network traffic, overall transaction will be more efficient and easier to perform business validation since all the data is contained in one transaction instead of being split out into different transactions.

However we still have not solved the data duplication issue yet, how we can solve? One approach to use the default Context Conduction mode used in HL7 CDA - inherit from certain attributes from parent unless being overridden at child level.

Let me try to illustrate the point below using my earlier example for HTTP PUT, instead of specifying resource reference such as patient and prescriber, instead we choose to allow these XML element to be null, or we can create another code for "dataAbsent" XML attribute to indicate it is not overridden at this level.

<?xml version="1.0" encoding="UTF-8"?>
<
CombinedMedicationPrescription>
    <prescriptionId><id>1.2.36.146595217.4.19.1.3141.20121202001</id></prescriptionId>
    <patient>  
        <type>Patient</type> <id>23453</id>
    </patient>
    <prescriber>
        <type>Agent</type> <id>567</id>
   </prescriber>
   <prescriptionReference/>  
   <prescriptionReference/>   

   <prescription>
      <prescriptionItemId/>

      <patient/>
      <prescriber/>
      <dispense>
          <dispenser/>
      </dispense>

      <medicine> .... . </medicine>
  </prescription>
   <prescription>
    <prescriptionItemId/>
       <patient/>
      <prescriber/>
      <dispense>
          <dispenser/>
      </dispense>

      <medicine> .... . </medicine>
  </prescription>
</
CombinedMedicationPrescription>

With the above design, then it will be very straightforward for client to compose the aggregated resource for batch PUT, for the common data elements such as patient, prescriber, date of prescription etc are specified once at aggregate resource level.

From FHIR core designers' perspective, they felt that the benefits of managing orders individually outweighs the overhead of repeating, and Context Conduction is kind of disaster in HL7v3.

I agreed that context conduction is over-complicated esp after the May 2011 ballot, but is it really possible to completely get rid of context conduction, esp the default one "OP" -  Inherit certain attribute from parent unless being overridden at child level? As a matter of fact it is very common in non-HL7v3 design environment.

Lets visualize how server side is going to process the request once it receives the batch PUT resource if the certain data elements such as prescriber and patient are repeated. When the process reaches resource reference such as prescriber at individual Prescription resource level, if the backend data model is one-to-one between prescription and medication,  then it is straightforward, it simply persists the cuurent resource data.

However if the data model is one-to-many where the prescriber is by default associated at parent level, then it needs to navigate up to batch level to check whether the prescriber at batch level is the same as the one in the current individual Prescription resource level. If prescriber value is the same, then it can safely persist the data. However if it is different, then it needs to create a new parent (primary key table) in the data model, and then persists the current resource data as its child (foreign key table), or it might just reject the whole message in this situation if business rule does not allow this kind of request where the prescriber is different between parent and child, but nevertheless it still needs to perform the above validation to ensure the received data comforms to the business validation rules.
So in nut shell, the server side still goes through the same processing mechanism similar to context conduction-"OP". Of course we can further simplify the context conduction, only propagation without the ability to override, then it is the main stream idea in typical relation database design such as order and order line item.

It is true that how data is persisted is outside FHIR concern or any exchange format design, but the design of exchange format needs to enable the efficient processing of the data on server wide regardless of the data model. With the current design, the FHIR resource is more efficiently processed if the data model is one-to-one between prescription and medicine, bulky if it is one-to-many between prescription and medicine. 

So this part of my concern still remains, what is your view?


Part 2 of series on Amazing power of HL7 FHIR and GenAI

For the past one week I have been working on part 2 of the series (refer to the overview of the series here - https://healthinterconnect.blo...