Introduction to the IBM FHIR Server
Welcome to the IBM implementation of the HL7 FHIR R4 specification. This project builds on our experience developing, operating, and maintaining a commercial-grade DSTU2 server implementation. Our goal is to provide a robust and performant R4 implementation that is suitable for production with minimal configuration; yet customizable for a wide range of use cases. In this article, we give you insight into the work that went into upgrading our server implementation for FHIR R4:
- Regenerating the model for R4 and refactoring the remaining code
- Validating and improving the conformance with the specification
- Evaluating the performance characteristics of the key components
The FHIR model component is the core of the server implementation. It provides Java APIs for parsing, building, generating, and validating FHIR resources. The Java model classes that represent FHIR resources and data types are generated directly from the structure definitions distributed with the specification. The IBM FHIR Server model objects differ from the Java reference implementation in that each model class implements the Java builder pattern (Effective Java, Joshua Bloch) for thread-safety and performance and the visitor pattern (GoF) to enable simple traversal logic (used internally for serialization, data copying, and more). All date/time processing is done using the Java 8 time library and the model classes implement Java equals, hashCode, and toString methods.
The model includes generated JavaDoc comments with key excerpts from the specification and many of the data type classes include additional factory methods to facilitate simple object construction. Model classes also include Java annotations for specification defined: constraints (@Constraint), required elements (@Required), choice element types (@Choice), and value set bindings (@Binding). Code elements with required ValueSet bindings are implemented as subclasses of Code with constant fields and nested enumerations. Backbone elements are implemented as Java nested classes to keep them organized.
All schema-level (structure, cardinality, value domain) and global (empty resource, empty element) constraint validation is performed during object construction, making it virtually impossible to build a schema-invalid FHIR resource. Additional constraint validation (invariants, profile, terminology) is performed using the FHIRValidator class. The FHIRParser and FHIRGenerator classes are used to parse and generate FHIR in either JSON or XML and FHIRPathEvaluator builds on an ANTLR4-generated parser to provide a performant FHIRPath evaluation engine that is used for validation and search parameter value extraction. Once the model, parser and core projects were stable, the remaining projects were refactored to work with the newly generated model.
Once we had a basic implementation, we used the AEGIS Touchstone Basic and Advanced test suites to test our compliance to the specification. In early testing we easily managed to get 100% conformance with the Basic test suite but the Advanced test suite result showed a 55% conformance. After adding support for JSON patch, the HTTP Prefer header, and performing a number of HTTP response code fixes, we obtained an overall conformance score of 98% (100% on Basic and 89% on Advanced). We are working towards full compliance on the Advanced test suite.
Performance has been a primary focus and has driven many of the decisions in the core server implementation and the database design. In the FHIR DSTU2 version that was developed internally and used in multiple offerings, we relied on JAXB (EclipseLink MOXy) for parsing/serialization and a combination of XML, XPath, XSLT, and Schematron for search parameter extraction and constraint validation. For FHIR R4, we took a new approach, opting to use specification driven code generation for both the model and the parsers and a custom FHIRPath implementation that operates over the model.
The FHIR Persistence component implements a pluggable persistence interface; the default JDBC implementation is tested with Apache Derby and IBM DB2. When DB2 is used, the JDBC implementation takes advantage of table partitioning and row-based access control in order to provide multi-tenancy with strong isolation, fast provisioning, and improved density over a schema-per-tenant design. Additionally, when paired with DB2, the JDBC persistence layer makes use of stored procedures to reduce the number of database round-trips during ingestion and makes careful use of literals and parameter-markers in queries to improve database cardinality estimates and ensure query plan stability.
The IBM FHIR Server combines examples from the FHIR specification with samples from the Team’s experience in order to drive a broad range of unit and integration tests. These tests can also be run standalone in order to probe the performance characteristics of the server and its components. The class com.ibm.fhir.server.test.app.Main in the fhir-server-test project runs through several thousand examples, performing a CREATE (POST) and a two READ (GET) operations for each resource.
Response time metrics are collected and collated into a simple report at the end of the run:
In the above run, all components (test driver, IBM FHIR Server and Derby database) were run locally on a mid-2015 MacBook Pro (2.5 GHz Intel Core i7, 16GB). Note that the client is also parsing and validating every resource it processes before submitting the request to the FHIR server.
We invite developers across health IT to leverage this server implementation to turbo-charge their adoption of FHIR R4, enhance data interoperability, and to collaborate with us on shaping the future of this project at https://github.com/ibm/fhir.