FHIR DSTU3 will be the primary method of data transfer between the Child and Parent nodes in a Sync network. Wherever possible we should default to using FHIR representations and rely on other methods only if OpenMRS objects cannot be represented using FHIR resources. Atom feeds used by Sync should point to FHIR urls for resources that are transmitted using FHIR.
The OpenMRS FHIR module will be extended to server both as a client and server that will be leveraged by Sync to create and read FHIR representations that will be used in pushing or pulling data. The FHIR services from the module will also be used by Sync to persist FHIR representations it retrieves from the Parent. Overall the module will be used in 3 ways in Sync:
1) On a Parent - as the server that receives data pushed from it's Children
2) As a client - on Children to both retrieve data from events published on their Parent, but also in their own atom feed. It will also be used to push data to their Parent.
3) As services to persist FHIR representations - on Children that retrieve FHIR resources from a Parent and need to persist them in OpenMRS.
Resource List
This is a list of domain objects that implement BaseOpenmrsData that we want to synchronize between OpenMRS servers. While we want to use FHIR as much as we can, it might come up that for some objects we will have to implement a different Sync protocol.
No | OpenMRS Entity | FHIR Resource | FHIR Maturity Level | Notes |
---|---|---|---|---|
1 | Patient | https://www.hl7.org/fhir/patient.html | 5 | |
2 | Obs | https://www.hl7.org/fhir/observation.html | 5 | |
3 | Encounter | https://www.hl7.org/fhir/encounter.html | 2 | |
4 | Visit | List of encounters can be considered: https://www.hl7.org/fhir/list.html | FHIR's Encounter resource can serve either as an OpenMRS Encounter or OpenMRS Visit. OpenMRS Encounter is a point in time.
| |
5 | Provider | https://www.hl7.org/fhir/practitioner.html | 3 | |
6 | Allergy | https://www.hl7.org/fhir/allergyintolerance.html | 3 | |
7 | Drug | https://www.hl7.org/fhir/medication.html | 3 | |
8 | Location | https://www.hl7.org/fhir/location.html | 3 | |
9 | Order | https://www.hl7.org/fhir/medicationrequest.html | FHIR Nutrition Order and MedicationRequest can be considered, but orders have a broader sense in OpenMRS. Priority would be MedicationRequest (Drug Order) and ProcedureRequest (Test Order) | |
10 | PatientProgram | https://www.hl7.org/fhir/episodeofcare.html | 2 | |
11 | PatientState | https://www.hl7.org/fhir/condition.html | 3 | |
12 | Person | https://www.hl7.org/fhir/person.html | 2 | |
13 | Relationship | https://www.hl7.org/fhir/relatedperson.html | 2 | |
14 | Cohort | https://www.hl7.org/fhir/group.html | 1 |
Synchronizing objects via FHIR
To add another object to FHIR synchronization, you must implement the FHIR Strategy Pattern
To do this, Firstly you need to create GenericObjectnameStrategy interface if you wish to synchronize Objectname object
It should look more/less like this:
package org.openmrs.module.fhir.api.strategies.objectname; import org.hl7.fhir.dstu3.model.Objectname; public interface GenericObjectnameStrategy { Objectname getObjectname(String uuid); void deleteObjectname(String uuid); Objectname updateObjectname(String uuid, Objectname objectname); Objectname createObjectname(Objectname objectname); }
As shown, it should have at least method stubs for getting, deleting, updatng and creating of Objectname object
After that, the interface needs to be implemented by ObjectnameStrategy class that would contain concrete implementatons of method stubs.
A Strategy Util class, like ObjectNameStrategyUtil should be created next to retrieve the implemented strategy in the following way:
package org.openmrs.module.fhir.api.strategies.objectname; import org.openmrs.api.context.Context; import org.openmrs.module.fhir.api.util.FHIRUtils; public class ObjectNameStrategyUtil { public static GenericObjectNameStrategy getObjectNameStrategy() { String strategy = FHIRUtils.getObjectNameStrategy(); return strategy == null ? new ObjectNameStrategy() : Context.getRegisteredComponent(strategy, GenericObjectNameStrategy.class); } }
Then, ObjectNameServiceImpl class needs to be created to call ObjectNameStrategyUtil class:
package org.openmrs.module.fhir.api.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hl7.fhir.dstu3.model.ObjectName; import org.openmrs.api.impl.BaseOpenmrsService; import org.openmrs.module.fhir.api.ObjectNameService; import org.openmrs.module.fhir.api.db.FHIRDAO; import org.openmrs.module.fhir.api.strategies.objectname.ObjectNameStrategyUtil; public class ObjectNameServiceImpl extends BaseOpenmrsService implements ObjectNameService { protected final Log log = LogFactory.getLog(this.getClass()); private FHIRDAO dao; /** * @return the dao */ public FHIRDAO getDao() { return dao; } /** * @param dao the dao to set */ public void setDao(FHIRDAO dao) { this.dao = dao; } @Override public ObjectName getObjectName(String uuid) { return ObjectNameStrageryUtil.getObjectNameStrategy().getObjectName(uuid); } @Override public void deleteObjectName(String uuid) { ObjectNameStrageryUtil.getObjectNameStrategy().deleteObjectName(uuid); } @Override public ObjectName updateObjectName(String uuid, ObjectName objectName) { return ObjectNameStrategyUtil.getObjectName().updateObjectName(uuid, objectName); } @Override public ObjectName createObjectName(ObjectName objectName) { return ObjectNameStrategyUtil.getObjectNameStrategy().createObjectName(ObjectName); } }
It should implement an ObjectNameService interface:
package org.openmrs.module.fhir.api; import org.hl7.fhir.dstu3.model.ObjectName; import org.openmrs.api.OpenmrsService; import org.springframework.transaction.annotation.Transactional; @Transactional public interface ObjectNameService extends OpenmrsService { /** * Get object by id * * @param uuid The uuid of object * @return ObjectName fhir resource */ ObjectName getObjectName(String uuid); /** * Delete object by id * * @param uuid The uuid of object */ void deleteObjectName(String uuid); /** * Update object * * @param uuid The uuid of object * @param objectName representation of object fhir resource */ ObjectName updateObjectName(String uuid, ObjectName objectName); /** * Create object * * @param objectName the object to create */ ObjectName createObjectName(ObjectName objectName); }
Also, a FHIRObjectNameResource needs to be created that is going to use the created Service:
package org.openmrs.module.fhir.resources; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.ObjectName; import org.openmrs.api.context.Context; import org.openmrs.module.fhir.api.ObjectNameService; public class FHIRObjectNameResource extends Resource { public ObjectName getByUniqueId(IdType id) { ObjectNameService objectNameService = Context.getService(ObjectNameService.class); return objectNameService.getObjectName(id.getIdPart()); } public void deleteObjectName(IdType id) { ObjectNameService ObjectNameService = Context.getService(ObjectNameService.class); objectNameService.deleteObjectName(id.getIdPart()); } public ObjectName updateObjectName(String id, ObjectName objectName) { return Context.getService(ObjectNameService.class).updateObjectName(id, objectName); } public ObjectName createObjectName(ObjectName objectName) { return Context.getService(ObjectNameService.class).createObjectName(objectName); } }
Last class that needs to be created is RestfulObjectNameProvider:
package org.openmrs.module.fhir.providers; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.IResourceProvider; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.RelatedPerson; import org.hl7.fhir.instance.model.api.IBaseResource; import org.openmrs.module.fhir.resources.FHIRRelatedPersonResource; import org.openmrs.module.fhir.util.MethodOutcomeBuilder; public class RestfulObjectNameProvider implements IResourceProvider { private FHIRObjectNameResource objectNameResource; public RestfulObjectNameProvider() { objectNameResource = new FHIRObjectNameResource(); } @Override public Class<? extends IBaseResource> getResourceType() { return ObjectName.class; } /** * Get object by unique id * * @param theId object containing the id */ @Read public ObjectName getResourceById(@IdParam IdType theId) { return objectNameResource.getByUniqueId(theId); } /** * Delete object by unique id * * @param theId object containing the id */ @Delete public void deleteObjectName(@IdParam IdType theId) { objectNameResource.deleteObjectName(theId); } /** * Update object by unique id * * @param theId object containing the id */ @Update public MethodOutcome updateObjectName(@ResourceParam ObjectName objectName, @IdParam IdType theId) { return MethodOutcomeBuilder.buildUpdate(objectName); } /** * Create object * * @param objectName fhir objectname object */ @Create public MethodOutcome createObjectName(@ResourceParam ObjectName objectName) { return MethodOutcomeBuilder.buildCreate(objectNameResource.createObjectName(objectName)); } }
The created provided must be added in FHIRRESTServer class in the initialize method to resourceProviders like this:
resourceProviders.add(new RestfulRelatedPersonProvider());
When all these classes are created in the FHIR code, the ObjectName object is prepared to be synchronized by FHIR