-
Notifications
You must be signed in to change notification settings - Fork 49
Architecture
HAPI FHIR Server is an open-source FHIR-based clinical data repository, exposing a FHIR-compliant REST API.
https://github.com/hapifhir/hapi-fhir
The CQF Ruler project is a set of modules for the HAPI FHIR Server that implement, among other things, CQL evaluation. The CQF Ruler project also provides an instance of the base HAPI FHIR Server preconfigured with the extended modules.
https://github.com/DBCG/cqf-ruler
The cqf-ruler uses the hapi-fhir-jpaserver-starter project as a base. On top of that, it adds an extensibility API and utility functions to allow creating plugins which contain functionality for a specific IG. This diagram shows how it's structured
Plugins use Spring Boot autoconfiguration to be loaded at runtime. Spring searches for a spring.factories
file in the meta-data of the jars on the classpath, and the spring.factories
file points to the root Spring config for the plugin. For example, the content of the resources/META-INF/spring.factories
file might be:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.ExampleConfig
Any Beans defined in that root plugin config that implement one the cqf-ruler plugin apis will be loaded by the cqf-ruler on startup. There's a full plugin example here.
Plugins should reference the cqf-ruler-core
project using the provided
scope. This tells Maven that the cqf-ruler-core
classes will be available at runtime, and not to include those dependencies in the plugin jar.
<dependency>
<groupId>org.opencds.cqf.ruler</groupId>
<artifactId>cqf-ruler-core</artifactId>
<version>0.5.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Any other dependencies also required by the base cqf-ruler-core
may also be listed in provided
scope
Currently the cqf-ruler recognizes three types of plugin contributions:
- Operation Providers
- Provide implementation of some FHIR operation
- Metadata Extenders
- Mutates the conformance/capability statements
- Interceptors
- Modify requests/responses
The plugin system is very simple and naive. Plugins are expected to be well-behaved, and not contribute any beans that may be invalid for the current server's configuration. This includes but is not limited to, multiple versions of plugins, mismatched FHIR versions, operation overrides, etc.
Embedded into the CQF Ruler Docker image is an instance of Jetty, which is a Java/JakartaEE compliant Java app server. The Jetty server self-configures based on the content of the CQF Ruler war. The configuration tells Jetty to use the Spring config classes to start the CQF Ruler and to map it to the /cqf-ruler/fhir endpoint.
Spring is a popular DI framework that's used throughout the HAPI FHIR Server and the CQF Ruler. Spring config files define a set of "Beans" which are instances of classes. The root configuration for the CQF Ruler is defined this way.
BaseServlet is a subclass of the standard HAPI FHIR Servlet that instantiates the CQF Ruler Operation Providers in addition to the standard HAPI Resource Providers
The HAPI Resource Providers implement the standard FHIR REST Operations, including resource CRUD, searching, transactions, etc.
- GET /Patient/123
- PUT /ValueSet/ABC
- GET /Observation?subject=123
The CQF Ruler does not implement its own data layer. Instead, it reuses the components available in the HAPI FHIR JPA Server project. Documentation for those components is available on HAPI's site: JPA Server Architecture
The HAPI FHIR Server provides an API for adding new FHIR operations to the server. That API is documented here: Plain Providers. The CQF Ruler uses that API to add new operations to the base HAPI FHIR Server.
The CQF Ruler operation providers implement various extended FHIR operations, with a particular focus on those related to CQL. The specifications for those operations are found in the FHIR Clinical Reasoning Module and CPG IG
The full set of providers can be seen at:
That set of providers is instantiated via Spring:
The specific operations on those providers are registered and mapped to REST APIs via an annotation framework defined by HAPI. An example can be seen here:
Together these comprise the core logic needed to implement CQL translation and evaluation.
Translates CQL to ELM, which is the machine readable representation of CQL logic. The translation process is already low-latency. The Atom plugin utilizes on-the-fly retranslation to support CQL authoring workflows. Work is on-going to make this even faster by reusing existing ELM in the translation process.
Executes ELM. The internal implementation uses a Visitor pattern to traverse an ELM graph and evaluate nodes and their children in order. Internally it supports expression caching such that it only traverses a given node once. Benchmarks show that running on a single AWS core with all required data and terminology cached in memory, a representative Measure takes less than <10ms to execute.
Integrates the CQL Translator and CQL Engine projects and provides implementations for several FHIR operations.
These are the main touchpoints that need to be understood and implemented to use the CQL stack on any given platform.
HAPI provides implementations for 3 of these out-of-the-box, while the cqf-ruler provides an implementation of the 4th.
TerminologyProvider: Github
DataProvider: Github
LibraryContentProvider: Github
FhirDal: Github
HAPI ships with most of the implementations of the CQL interfaces necessary to run CQL on the HAPI platform. HAPI also includes an ELM caching layer that caches ELM on the first translation of CQL, and expires the cache whenever the underlying FHIR library resource has changed.
JPATerminologyProvider: Github
JPAFhirRetrieveProvider: Github
NOTE: RetrieveProvider is a subset of the DataProvider interface.
LibraryContentProvider: Github
FhirDal: Github
ELM Cache: Github
The following diagrams illustrate how the components interact for several operations. These are ordered from least to most complex:
- $cql
- Library $evaluate
- Measure $evaluate-measure
The $cql is defined in the CPG IG. It takes a single CQL expression as text, and returns a FHIR type or types depending on the expression.
The $cql operation is the simplest implementation of CQL evaluation in the CQF Ruler but utilizes most of the components. This operation is not reused internally in the CQF Ruler anywhere or by the FHIR specification to support any other operation. It's mainly used as a way to evaluate one-off CQL expressions in environments that are not able to use the CQL authoring toolkit.
The Library $evaluate operation is also defined in the CPG IG. It computes the set of results for the expressions defined in the CQL Library embedded within a FHIR Library, and returns the set as a FHIR Parameters Resource. It's higher-level than the cql operation in that it operates on a FHIR Library resource that has CQL content embedded, and it can returns results for a set of expressions.
Library $evaluate is implemented in two layers:
- FHIR Library processing
- CQL evaluation
Both layers are required to fully implement the operation as specified in the CPG IG. Libraries technically need not contain CQL, but within the context of Clinical Reasoning (e.g. cds-hooks, PlanDefinition processing, Measure evaluation, etc.) they usually do.
The Measure $evaluate-measure operations is defined in the FHIR Clinical Reasoning Module. It generates a MeasureReport for a specified Measure over a set of subjects given a reporting period.
One important aspect of Measure evaluation is that there are two layers of processing involved.
- FHIR Measure processing
- CQL evaluation
The Measure specification allows the criteria used for each of the Population, Supplemental Data, and Stratifier elements to be expressed using FHIRPath instead of CQL. This means that technically CQL is not required for a simple Measure evaluation environment. However, specific profiles of Measure such as those defined by the CQF Measures IG may require CQL. In practice, FHIR Measures are nearly always specified with CQL.
The desire to simplify the implementation of Measure evaluation, to support data models other than FHIR (such as QDM), and to, in the future, support FHIRPath as an expression language motivated the two-layer approach to Measure processing. PlanDefinition $apply, which is used in conjunction with CQL to support FHIR-based Clinical Decision Support, has many of the same considerations.
There are a number of other CQL-related operations defined for various use cases, including:
- $data-requirements
- PlanDefinition $apply
- Measure $care-gaps