Commure Structured Data Capture API
The concept of Structured Data Capture (SDC) is built on top of the FHIR Questionnaire and QuestionnaireResponse resources. Those resources enable the creation of configurable forms that define an optimal data structure for capturing input from users, ideal for data capture.
The idea of SDC offers at least the following benefits:
- Advanced dynamic questionnaires including calculated scores, enabling of questions and groups based on complex logic, drop-downs driven by answers to other questions
- Support for populating questionnaires from existing clinical data based on the current patient, encounter, etc., including the ability to render the form as HTML or to redirect to a website to complete unanswered items
- Support for extracting info from a completed QuestionnaireResponse to generate other FHIR resources such as Observations, MedicationStatements, etc.
- Support for completing 'adaptive' questionnaires
Commure is focused on the SDC use cases presented by (2) and (3).
Why use a FHIR Questionnaire and QuestionnaireResponse?
A Questionnaire serves as a kind of schema for producing a QuestionnaireResponse, which
holds the results of an answer to a Questionnaire. A Questionnaire can define any number
of questions (called items
), each of which can collect data of various types
and be
arranged in groups
that make hierarchical sense.
It makes sense to use a Questionnaire when your use case is to store data collected from a user, as they answer some series of (either related or unrelated) questions.
Read More:
What does SDC enable that a Questionnaire and QuestionnaireResponse cannot do alone?
Questionnaires are very flexible FHIR Resources: they can hold as much or as little data as your data entry use case dictates. And this data can be ordered and grouped in a way that makes sense for a user in the relevant context.
Having captured data in this format, it is likely that you will want to translate
the arbitrary QuestionnaireResponse items
into more meaningful, standalone FHIR
resources. That translation process is what Structured Data Capture can facilitate.
When should I use Structured Data Capture?
Structured Data Capture can introduce a lot of overhead and complexity. So the first question worth answering is: do I really need Structured Data Capture?
If your application is not producing data that needs to be interoperable with external systems, you can probably skip Structured Data Capture.
For example:
- If your users intend to physically print out their QuestionnaireResponses and hand them off to someone to perform clinical work — and the data in those QuestionnaireResponses will never be used by any digital user again — your data doesn't need to be interoperable.
- If your application is very self-contained — if the Questionnaires ask specific questions to which the answers have no place in the FHIR data model, and no code beyond your application code will have a need to interact with the data being entered — then you probably lose little by leaving your data non-interoperable.
On the other hand, if you are hoping to build an application that is the source of data that is used by other applications in the health systems to which you are deployed, you'll likely want to use Structured Data Capture.
For example: if you have a Questionnaire that asks for a patient's first name and last name,
that is likely a valuable piece of information to store in a FHIR Patient - in Patient.name
.
You can use SDC to perform the transformation of a QuestionnaireResponse.item
into that Patient.name
.
Opting out of doing SDC on this patient name means that your app will leave the data in only your
specific QuestionnaireResponse.item
. It is unlikely that other applications will take the time
to figure out exactly where this piece of data lives and how to use it. They are more likely
to (appropriately) assume that the most up-date information lives in the Patient resource, at
Patient.name
.
Whether you should adopt SDC in your specific use case can vary based on the scope and long-term intent of your project, so consider it carefully.
Read More:
What parts of SDC does Commure offer?
Commure offers API endpoints that implement the FHIR extended operations $extract
and $populate
.
$extract
exists to take a QuestionnaireResponse
as input, and produce a set of other FHIR Resources
as output.
Commure's implementation uses definition-based extraction, which means that the mapping of certain
Questionnaire items
to FHIR Resource fields is encoded in fields (oriented around the definition
field) on the Questionnaire itself.
$populate
exists to take a Questionnaire
and set of other FHIR Resources as input, and output
a QuestionnaireResponse
that is pre-populated with information from the aforementioned FHIR Resources.
Commure's implementation uses expression-based population, which means that the mapping of
those input FHIR Resources to pre-populating specific items
in the QuestionnaireResponse is
encoded in the corresponding Questionnaire. You can understand what fields get pre-populated with
what values by reviewing fields like the questionnaire-initialExpression
extension.
Read More:
What architecture/workflow is appropriate for SDC?
A typical flow for using $populate
and $extract
would look something like the following:
- User clicks UI element that is expected to open a form for data entry input
- UI fetches a set of FHIR Resources relevant to the data the user is about to enter
- UI calls
$populate
to pre-fill the form with information we already know - User performs data entry on the QuestionnaireResponse
- UI POST's the completed QuestionnaireResponse for persistence
- UI calls
$extract
with the completed QuestionnaireResponse - UI submits the extracted resources for persistence
The lion's share of complexity and possibility for issues — given the experimental nature of
Commure's SDC tooling — occurs at step 6 and 7. It would be wise to consider decoupling
the concerns of capturing QuestionnaireResponse data vs. transforming that captured
data into interoperable resources. For example, you could persist the QuestionnaireResponse,
and then run an asynchronous job that independently processes completed QuestionnaireResponses
through the $extract
endpoint.
This asynchronous approach is not always an option. For example, if your application collects information about a patient's name, and then immediately expects the header of the page to update with the newly-input patient's name, it is unlikely that you will want your page header to be querying for the latest QuestionnaireResponse from your app (as opposed to querying directly for the FHIR Patient)
Read More: