I am working in FHIR APIs. I have a PatientService interface and 2 implementation classes such as DSTU2PatientService, STU3PatientService.
Our client has an implemented FHIR DSTU2 API for demographics, whereas Procedure is in STU3.
My use case is, how to distinguish which service( DSTU2/STU3 ) should be called when request comes from the patient to get their health data from EHR system.
How to include mediator pattern to achieve the call dynamically? I don't want to use if
condition.
fhir.demogrphics=DSTU2
fhir.procedure=STU3
public interface FHIRPatientService {
Object getDemographics(PatientDTO patient);
Object getProcedures(PatientDTO patient);
}
I have integrated the FHIR DSTU2 API DSTU2PatientService
.
@Service(value = "dstu2PatientService")
public class DSTU2PatientService implements PatientService {
private static final Logger LOG = LoggerFactory.getLogger(DSTU2PatientService.class);
private FhirContext fhirContextDstu2;
@Autowired
private FHIRConfig fhirConfig;
@Autowired
private BasicAuthInterceptor authInterceptor;
public DSTU2PatientService(@Qualifier("fhirContextDstu2") FhirContext fhirContextDstu2) {
this.fhirContextDstu2 = fhirContextDstu2;
}
@Override
public Object getDemographics(PatientDTO patient) {
Bundle bundle = null;
try {
IGenericClient clientDstu2 = fhirContextDstu2.newRestfulGenericClient(fhirConfig.getFhirServerPathDstu2());
clientDstu2.registerInterceptor(authInterceptor);
bundle = clientDstu2.search()
.forResource(Patient.class)
.where(Patient.GIVEN.matches().value(patient.getGiven()))
.and(Patient.FAMILY.matches().value(patient.getFamily()))
.and(Patient.BIRTHDATE.exactly().day(patient.getBirthdate()))
.and(Patient.ADDRESS.contains().value(patient.getAddress()))
.and(Patient.GENDER.exactly().codes(patient.getGender()))
.returnBundle(Bundle.class)
.execute();
}catch(Exception e){
LOG.error("Demographics: {}", e.getMessage());
bundle = new Bundle();
}
return bundle;
}
@Override
public Object getProcedures(PatientDTO patient) {
Bundle bundle = null;
try {
IGenericClient clientDstu2 = fhirContextDstu2.newRestfulGenericClient(fhirConfig.getFhirServerPathDstu2());
clientDstu2.registerInterceptor(authInterceptor);
clientDstu2.registerInterceptor(CommonUtil.headersInterceptor(patient.getMychartId()));
bundle = clientDstu2.search()
.forResource(Procedure.class)
.where(new ReferenceClientParam("patient").hasId(patient.getSubject()))
.and(Procedure.DATE.afterOrEquals().day(patient.getStartDate()))
.and(Procedure.DATE.beforeOrEquals().day(patient.getEndDate()))
.returnBundle(Bundle.class)
.execute();
}catch(Exception e){
LOG.error("Procedures: {}", e.getMessage());
bundle = new Bundle();
}
return bundle;
}
}
I have integrated the FHIR STU3 API STU3PatientService
.
@Service(value = "stu3PatientService")
public class STU3PatientService implements PatientService {
private static final Logger LOG = LoggerFactory.getLogger(STU3PatientService.class);
private FhirContext fhirContextStu3;
@Autowired
private FHIRConfig fhirConfig;
@Autowired
private BasicAuthInterceptor authInterceptor;
public STU3PatientService(@Qualifier("fhirContextStu3") FhirContext fhirContextStu3) {
this.fhirContextStu3 = fhirContextStu3;
}
@Override
public Object getDemographics(PatientDTO patient) {
Bundle bundle = null;
try {
IGenericClient clientStu3 = fhirContextStu3.newRestfulGenericClient(fhirConfig.getFhirServerPathStu3());
clientStu3.registerInterceptor(authInterceptor);
bundle = clientStu3.search()
.forResource(Patient.class)
.where(Patient.GIVEN.matches().value(patient.getGiven()))
.and(Patient.FAMILY.matches().value(patient.getFamily()))
.and(Patient.BIRTHDATE.exactly().day(patient.getBirthdate()))
.and(Patient.ADDRESS.contains().value(patient.getAddress()))
.and(Patient.GENDER.exactly().codes(patient.getGender()))
.returnBundle(Bundle.class)
.execute();
}catch(Exception e){
LOG.error("Demographics: {}", e.getMessage());
bundle = new Bundle();
}
return bundle;
}
@Override
public bundle getProcedures(PatientDTO patient) {
Bundle bundle = null;
try {
IGenericClient clientStu3 = fhirContextStu3.newRestfulGenericClient(fhirConfig.getFhirServerPathStu3());
clientStu3.registerInterceptor(authInterceptor);
clientStu3.registerInterceptor(CommonUtil.headersInterceptor(patient.getMychartId()));
bundle = clientStu3.search()
.forResource(Procedure.class)
.where(new ReferenceClientParam("patient").hasId(patient.getSubject()))
.and(Procedure.DATE.afterOrEquals().day(patient.getStartDate()))
.and(Procedure.DATE.beforeOrEquals().day(patient.getEndDate()))
.returnBundle(Bundle.class)
.execute();
}catch(Exception e){
LOG.error("Procedures: {}", e.getMessage());
bundle = new Bundle();
}
return bundle;
}
}
@Component(value = "fhirService")
public class FHIRComponent {
private static final Logger LOG = LoggerFactory.getLogger(FHIRComponent.class);
private FHIRResourceVersionConfig fhirResourceVersionConfig;
private PatientService dstu2PatientService;
private PatientService stu3PatientService;
public FHIRComponent(
@Qualifier("dstu2PatientService") FHIRPatientService dstu2PatientService,
@Qualifier("stu3PatientService") FHIRPatientService stu3PatientService,
FHIRResourceVersionConfig fhirResourceVersionConfig) {
this.dstu2PatientService = dstu2PatientService;
this.stu3PatientService = stu3PatientService;
this.fhirResourceVersionConfig = fhirResourceVersionConfig;
}
public Object getDemographics(PatientDTO patient, String resourceType) {
Object result = null;
if("DSTU2".equalsIgnoreCase(fhirResourceVersionConfig.findResource(resourceName)))
result = patientServiceDstu2.getDemographics(patient);
else
result = patientServiceStu3.getDemographics(patient);
return result;
}
public Object getConditions(PatientDTO patient) {
Object result = null;
if("DSTU2".equalsIgnoreCase(fhirResourceVersionConfig.findResource(resourceName)))
result = patientServiceDstu2.getConditions(patient);
else
result = patientServiceStu3.getConditions(patient);
return result;
}
}
You need to make the FHIRPatientService
aware for which code it is responsible:
public interface FHIRPatientService {
String getCode();
Object getDemographics(PatientDTO patient);
Object getProcedures(PatientDTO patient);
}
Then you can refactor your component
@Component(value = "fhirService")
public class FHIRComponent {
private static final Logger LOG = LoggerFactory.getLogger(FHIRComponent.class);
private FHIRResourceVersionConfig fhirResourceVersionConfig;
private List<PatientService> patientServices;//spring will inject all services
public FHIRComponent(
List<PatientService> patientServices,
FHIRResourceVersionConfig fhirResourceVersionConfig) {
this.patientServices= patientServices;
this.fhirResourceVersionConfig = fhirResourceVersionConfig;
}
private Optional<PatientService> getService(String resourceType){
return patientServices.stream()
.filter(service => service.getCode().equalsIgnoreCase(fhirResourceVersionConfig.findResource(resourceName)))
.findAny()
}
public Object getDemographics(PatientDTO patient, String resourceType) {
return getService(resourceType)
.map(service => service.getDemographics(patient))
.orElse(null);
}
...
I hope my explenation is a bit clear...