Skip to content

Architecture

Jonathan Percival edited this page Mar 1, 2022 · 7 revisions

Overview

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

Module Structure

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

Module Diagram

Plugins

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

Plugin API

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

Plugin Limitations

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.

CQF Ruler Architecture

CQF Ruler

Jetty

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.

Github Dockerfile

Github Jetty Config

Spring

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.

Github

BaseServlet

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

Github

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

HAPI JPA, Terminology Service, Searching, Indexing

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

CQF Ruler Operation Providers

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:

Github

That set of providers is instantiated via Spring:

Github

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:

Github

Core CQL Modules

Together these comprise the core logic needed to implement CQL translation and evaluation.

CQL Translator

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.

Github

CQL Engine

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.

Github

CQL Evaluator

Integrates the CQL Translator and CQL Engine projects and provides implementations for several FHIR operations.

Github

CQL Interfaces

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 CQL Interface Implementations

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

Operations

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

$cql Operation

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.

Sequence Diagram

$cql

Notes

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.

Library $evaluate

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 Sequence Diagram

Library $evaluate

Library $evaluate Notes

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.

Measure $evaluate-measure

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.

Measure $evaluate-measure Sequence Diagram

Measure $evaluate-measure

Measure $evaluate-measure Notes

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.

Other Clinical Reasoning Operations

There are a number of other CQL-related operations defined for various use cases, including:

  • $data-requirements
  • PlanDefinition $apply
  • Measure $care-gaps
Clone this wiki locally