Structured Data Capture
4.0.0 - STU 4 International flag

Structured Data Capture, published by HL7 International / FHIR Infrastructure. This guide is not an authorized publication; it is the continuous build for version 4.0.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/sdc/ and changes regularly. See the Directory of published versions

Modular Forms

Page standards status: Trial-use

Modular Questionnaire Background

Maintaining questionnaires can take a considerable amount of effort, particularly if they include logic to support population or extraction. At the same time, it is common for questionnaires to share content - the same questions or even the same sections might appear in multiple forms. Some organizations, particularly those with a research focus may have extensive libraries of questions intended to help drive re-use and consistency. This allows those organizations to increase consistency in how data is collected, which in turn increases the comparability of data captured, even when captured using different instruments. It also increases the quality of the data collected by encouraging the re-use of questions that have been vetted for readability, neutral phrasing and other quality considerations.

Achieving re-use with questionnaires is primarily focused on the benefit of authors. The use of modularization techniques is often transparent to the end users who must complete the questionnaires, unless they find themselves filling out many distinct forms and happen to notice the consistency of language and sections between those forms. In other words, questionnaire re-use is part of the authoring and publishing process, but generally not the form filling process.

This portion of the SDC specification describes three mechanisms for enabling re-use:

  • Data Element-based Questionnaires are crafted by having items make use of the definition element. The referenced definitions include all of the metadata for the question, group or display item including the text, data type, optionality and other characteristics. Questionnaires are authored by assembling these minimal items referencing robust definitions.
  • Modular Questionnaires allow one questionnaire to be composed of sub-questionnaires (which can in turn be composed of further sub-questionnaires, and so on).
  • Questionnaire Library Referencing allows any item in a Questionnaire to reference a specific item in another Questionnaire using the format Questionnaire URL#linkId. This enables reuse of a specific item and its descendants (if any) without the limitations of StructureDefinition-based referencing. This is the recommended method going forward.

When using the data element-based approach, every single 'item' in the questionnaire must be specified, including all 'display' items, groups, etc. Re-use is limited to question text, value set, data type and other information that can be determined from the referenced definition element. When using the sub-questionnaire approach, multiple items can be defined along with display text, enableWhen logic and other questionnaire characteristics. The first approach is best suited for "data-element"- based questionnaires and the latter for defining collections of questions. (While defining separate modules for every single question is possible, it would be quite a bit of overhead).

These reuse mechanisms are not mutually exclusive. It is possible to create forms that mix all three techniques—for example, using subQuestionnaire for group-level reuse, as well as definitions that point to both StructureDefinition#elementId and Questionnaire#linkId for individual items.

Modular workflow

Profile: Modular Questionnaire profile
Relevant Extensions:
Example Questionnaires: See below

Regardless of mechanism, there are two phases. First, the questionnaire is authored in its modular form, maximizing re-use and minimizing authoring effort. Then, there is then a need to take the re-useably authored form (or collection of forms) and generate a fully 'assembled' form that contains all of the details needed for a Form Filler to properly render and capture answers for the form. While this assembly process can be undertaken by the Form Filler, it is more typically managed by the form designer as part of the publication process.

The following diagram shows the results of the assembly process with a set of questionnaires that combine both approaches:

One parent questionnaire referencing two sub-questionnaires, which each in turn reference elements in a StructureDefinition,            followed by the resulting single assembled questionnaire

The first questionnaire (Q1) contains two items, each with a sub-module extension pointing to other questionnaires - Q2 and Q3. Those two sub-questionnaires in term make use of the 'definition' element to refer to data elements from a single StructureDefinition. (In practice, the elements could as easily have been pointed to from a variety of StructureDefinitions.) The assemble operation then produces a new instance of Q1 that combines all of the items referenced from the two sub-modules and brings in the question text and other metadata from the referenced element definitions.

The assembly process can be done internally or by invoking the $assemble operation. Questionnaires might use any combination of defining content traditionally (i.e. all item details defined inline), referencing sub-questionnaires, and/or referencing external data elements. There is also no theoretical limit to the amount of nesting that can happen with sub-questionnaires, though practically more than 2-3 levels of nesting are very unlikely.

When working with modular questionnaires, the 'assembly' step should be performed prior to any population steps.

Finding and distinguishing modular/assembly-relevant forms

It is helpful to know when a Questionnaire is going to require assembly or not. It is also helpful to know whether a particular questionnaire can be used as a 'root' form for entry, can be used as a sub-module or either. A system can search through for items that declare the subQuestionnaire extension, though doing so isn't terribly efficient. However, for Data Element-based Questionnaires, there is no mechanism to tell with certainty that assembly is required - the 'definition' element may be included in questions for a variety of reasons and some degree of metadata may be maintained in the 'master' Questionnaire for readability. Therefore, this specification defines a assemble-expectation extension that allows flagging whether a particular Questionnaire requires assembly, whether it is safe to use as a subQuestionnaire and/or whether it can be used as a 'root' Questionnaire.

code $assemble before use? Can be subQuestionnaire? Can be root questionnaire?
assemble-root-or-child Yes Yes Yes
assemble-root Yes No Yes
assemble-child Yes Yes No
independent-root-or-child No Yes Yes
independent-child No Yes No

There is no code for a questionnaire that's intended for use as a stand-alone form, doesn't require assembly and isn't intended for use as a child form, because that's the default.

In addition to these codes, the code system has a few additional abstract codes that are only available to aid in searching:

  • assembly encompasses all codes that mean assembly is required and independent encompasses all codes that don't require assembly (though it won't include Questionnaires where the extension isn't present at all). E.g. to find all Questionnaires that don't require assembly, you would search Questionnaire?assemble-expectation:below=independent as well as Questionnaire?assemble-expectation:missing=true. Alternatively, if 'missing' and 'below' aren't supported, you could simply use Questionnaire?assemble-expectation:not=assemble-root-or-child,assemble-root,assemble-child.
  • root encompasses all codes that mean the form can be the 'root' for data entry. To find all Questionnaires that can be the root for data capture, you would search Questionnaire?assemble-expectation:below=root as well as Questionnaire?assemble-expectation:missing=true. Alternatively, if 'missing' and 'below' aren't supported, you could simply use Questionnaire?assemble-expectation:not=assemble-child,independent-child.
  • child encompasses all codes that mean the form can be used as a sub-module. To find all Questionnaires that can be safely used as sub-modules, you would search Questionnaire?assemble-expectation=assemble-root-or-child,assemble-child,indelpendent-root-or-child,independent-child.

Modular Questionnaires

The notion of a modular Questionnaire is that a 'display' item in a parent questionnaire can include an extension pointing to a specific Questionnaire whose items should be embedded in the resulting assembled Questionnaire in place of the 'display' item. The details of how this works are as follows:

  1. The subQuestionnaire extension will contain the canonical reference to the Questionnaire whose content should be substituted for the display item
  2. The canonical reference SHOULD be version-specific to ensure that the author of the parent questionnaire has full control over what content they are importing. If the subQuestionnaire is not version-specific, then the same version of the parent Questionnaire could include varying items as the subQuestionnaires evolve and this is considered an error. I.e. if canonical references are not version-specific, the content of the referenced Questionnaires cannot change in a way that results in differences in allowed QuestionnaireResponses.
  3. It is possible that the assembly option will be unable to substitute the display item for the referenced Questionnaire or that the system that tries to make use of the modular Questionnaire might not recognize or be able to support assembly of the Questionnaire at all. For this reason, the 'text' of the display element SHOULD say something that is meaningful in situations where it is not replaced. For example: "Sub-questionnaire [some URL] not available. Unable to display all questions."
  4. When the Questionnaire goes through the 'assembly' process, the display item having the subQuestionnaire extension will be removed entirely. I.e. that linkId and the associated text will not appear in the assembled Questionnaire. Instead, all of the 'root' items of the referenced Questionnaire - including their descendant elements and items will appear in the same position as the replaced 'display' item was.
  5. SubQuestionnaires are not allowed to be adaptive - i.e. it is an error if they have the sdc-questionnaire-questionnaireAdaptive extension.
  6. Before assembly, the process must check for agreement between the parent Questionnaire and subQuestionnaire for the items listed below. It's fine if the subQuestionnaire is missing any of these elements. However, if any elements are present, the values in the subQuestionnaire must match or be a proper subset of the values in the parent Questionnaire.
  7. The engine SHOULD raise an error if the subQuestionnaire contains any modifier extensions, though if the modifier extensions are recognized by the engine and are known to be safe to propagate, the engine MAY do so
  8. In addition to propagating the items from the referenced Questionnaire, additional items are also propagated as follows:
    • All elements checked for alignment above will not be propagated. (They don't need to be, as they're already aligned.)
    • Questionnaire.meta elements other than those listed above will also not be propagated.
    • Any contained resources in the subQuestionnaire are added as contained resources in the parent Questionnaire with the exception that, barring linkIdPrefix requirements (see further below), if a subQuestionnaire is imported more than once, contained resources will only be included once in the assembled Questionnaire.
    • Extensions found at the root of the subQuestionnaire are propagated differently, depending on the nature of the extension:
    • All elements on the subQuestionnaire not previously mentioned other than Questionnaire.item (i.e. description, useContext, copyright, etc.) are ignored. If there are copyright limitations on the referenced Questionnaire, it is the responsibility of the referencing Questionnaire to reflect those in its own Questionnaire.copyright element.
    • The assemble-expectation on the resulting Questionnaire should be adjusted to change the 'assemble' part of the code to 'independent', with the exception that if the code was 'assemble-root', then the extension should be removed entirely. E.g. assemble-root-or-child would change to independent-root-or-child.
  9. This process occurs recursively. I.e. If an imported Questionnaire contains display elements with a subQuestionnaire extension, those display elements are also replaced by the referenced Questionnaire, repeating the process until no further subQuestionnaire extensions are found.
  10. It is possible that the same Questionnaire might be substituted more than once as part of this process, however it is in an error if the module references recurse. E.g. if Questionnaire A contains an item that references subQuestionnaire B, which in turn has an element that references subQuestionnaire A.
  11. It is also an error if the resulting fully-assembled Questionnaire has any duplicate linkIds.
  12. In order to avoid duplicate linkIds, a parent Questionnaire MAY declare a special variable with the name linkIdPrefix. If there is a linkIdPrefix in context at the time a subQuestionnaire is substituted, that linkIdPrefix SHALL be pre-pended to the linkId and enableWhen.question elements of all items in that Questionnaire. See the examples to see how this works in practice. If linkIdPrefix is not used, care should be taken to ensure that linkIds are appropriately coordinated to avoid overlap across all referenced Questionnaires
  13. LinkIdPrefixes are also prepended to the 'id' elements of any contained resources and all references to contained resources (i.e. references or canonicals that start with '#').
  14. linkIdPrefixes will need to be referenced in any expressions that are dependent on linkId. For example, if there is an expression that says %root.descendants().select(item.where(linkId='1.1')) would need to change to %root.descendants().select(item.where(linkId=%linkIdPrefix + '1.1')) This will need to happen in all extensions with a type of expression.
  15. Imported Questionnaires may be defined to be dependent on contextual information passed in from the referencing Questionnaire (including 'linkIdPrefix'). To allow validating these subQuestionnaires independent of their inclusion in a parent, all such dependencies must be declared using the assembleContext extension on the root of the Questionnaire. As well, if a Questionnaire is referenced by a subQuestionnaire extension, it is an error if the listed variables are not available in the context of the referencing element.
  16. The presence of an assembleContext extension on a Questionnaire indicates that it can ONLY be used as a part of a modular Questionnaire.
  17. If stored, an assembled Questionnaire SHALL have the same URL and Version as the base Questionnaire. Note that this means that it's possible for a server to have multiple Questionnaire instances that share the same URL and Version. Clients SHOULD populate the knowledge-capability and SHALL populate it if they know that both unassembled and assembled versions will be hosted on the same server. This extension allows differentiating between assembled (executable) from non-assembled (computable) instances. As part of the assembly process, the assembly engine SHOULD add assembledFrom extensions pointing to the specific versions of all descendant Questionnaires that were used as part of the assembly process. The root questionnaire doesn't need to be listed as its URL and version will be the same as the assembled Questionnaire.

    In situations where there can be more than one assembled version of the same questionnaire version hosted on the same server, the assembly process SHALL also populate package-source (and the package information that can be gleaned from it) to help differentiate between multiple assembled instances of a Questionnaire. This will typically only happen where the Questionnaire component hierarchy isn't all version-specific.

We will set an expectation in SDC about what changes are permitted for 'active' questionnaires while keeping version unchanged and which changes force a new version.

Data Element-based Questionnaires

⚠️ Warning: This implementation guide previously supported referencing items from StructureDefinition resources using Questionnaire.item.definition. While still technically supported, this approach is limited:

  • It does not map well to all Questionnaire.item features (e.g., display, text, answerOption).
  • It is not widely supported by Questionnaire authoring tools.

Future versions of this guide may remove support for the StructureDefinition-based approach. It is recommended to transition to the Questionnaire Library Referencing wherever feasible.

Data Element-based Questionnaires rely on the fact that Questionnaire 'item' elements largely correspond to the information found in the ElementDefinitions that are part of StructureDefinitions. These might be FHIR resources or data types, FHIR profiles, or logical models that represent any type of data at all. Full details on the mappings between Questionnaire.item and StructureDefinition.snapshot.element as well as general guidance on how definitions are to be mapped, including mapping to version-specific artifacts, mapping to slices, etc. are found in the FHIR core specification here.

The process for 'assembling' a Questionnaire that leverages Data Element-based mappings is as follows:

  1. For each item in the questionnaire, if an item.definition element is present, try to resolve the referenced element.
  2. If the URL refers to an element in a StructureDefinition and the StructureDefinition resolves but the referenced element does not, the system SHOULD raise an error.
  3. Otherwise, if the referenced element cannot be resolved, raise a warning unless the item has no text element or the item.type is 'choice' or 'open-choice' and no answerOptions, answerValueSet or answerExpression is available, in which case raise an error.
  4. Go through each property and extension on the element and see if the corresponding property or extension already exists on the Questionnaire.item. (Extensions are matched by URL and then 'value.name' if the extension is an expression.)
    • If the property or extension is not present (and the property has a mapping to a Questionnaire.item property or the extension is one allowed to appear on Questionnaire.item), then propagate the property or extension to the Questionnaire.item.
    • If the property or extension already exists, leave it as is, but provide a warning if the content in the element definition differs from that on the Questionnaire.item. I.e. Elements defined on the Questionnaire item override information found on the referenced definition, but a warning will be raised noting any discrepancies.
  5. If any of the properties include references to contained resources (e.g. a value set referenced by a binding), those resources should also be propagated. The Form Filler might need to re-assign the contained resource id (and update references) to avoid id collision. A given contained resource should only be propagated into the assembled Questionnaire once.
  6. If the item.type is 'group' and the group has no child items with a type other than 'display', then child question items of the appropriate type should be generated for each child element of the ElementDefinition pointed to by the item.definition. This process recurses. The linkId of the generated items should be a concatenation of the linkId of the group and the path of the element.

    If assembling a 'group' item from a definition, the following rules apply:

    • The linkId of the child questions are taken as the relative paths from the id of the elements in the definition. For example, if a group with linkId 3.2 has a definition that points to Patient.contact, the child questions would have linkId values of 3.2.name, 3.2.address, 3.2.address.line, etc.
    • Elements with maxOccurs of 0 or elements with fixed values or patterns will not be included as items.
    • When generating questions, the id, extension, and modifierExtension elements are not generated as child questions, though slices of them will be.
    • If the model has slices, each slice is treated as a separate item in the assembled Questionnaire.
    • If slicing is not closed, for elements other than extension and modifierExtension, additional items will be added to support "other" values. For example, if a definition pointed to USCore Organization.identifier, there would be groups for the NPI, CLIA, and NIAC identifiers, but there would also be a group for "other identifiers" to handle identifiers not covered by one of the slices. (linkIds would be something like 4.5.identifier:NAIC.value, 4.5.identifier:NIAC.period.start for the slices, and 4.5.identifier.value for the "other" items.)

    NOTE: While generating complex structures of groups and questions from a FHIR resource profile is possible, in most situations, pointing to logical models specifically designed to be "Questionnaire-friendly" will produce better results.

  7. Alternatively, if the item.type is 'group' and the group does have child questions, the child questions should be matched against the children of the ElementDefinition (using item.definition) and any ElementDefinition child elements not present as child questions should produce a warning.

If a system wants to create a library of 'questions' that can be drawn on by Questionnaires, this can be accomplished by:

  • using a single StructureDefinition as a logical model with elements for each item;
  • creating a distinct StructureDefinition for each item; or
  • a combination where there are multiple StructureDefinition 'sub-libraries', each with a collection of elements

The approach taken will depend on whether metadata such as status, publisher, etc. needs to be tracked on a per element basis or can be tracked at a higher level of granularity. Note that even when creating a separate data model for each item, some items may be 'complex', representing a 'group' with multiple child questions, and thus there will still be multiple elements in the StructureDefinition.

The only thing this implementation guide adds to the capabilities described in the base specification is the formal definition of the $assemble operation, including expectations of behavior for situations when the properties for an item in the base Questionnaire differ from those in the referenced definition.

Questionnaire Library Referencing

This section introduces a preferred method for managing reusable items—such as questions, groups, and displays—by referencing them from other Questionnaires during the form assembly process. Instead of duplicating content, implementers can now reference a specific item from another Questionnaire using the format: [Questionnaire URL]#[linkId]

This mechanism allows reuse of complete item definitions, including display text, data types, extensions, and constraints.

While this implementation guide previously supported referencing item definitions from StructureDefinition using item.definition, that approach is limited and may not represent all Questionnaire.item capabilities. This newer method offers a more robust and authoring-friendly alternative.

Note: If the referenced item has child items, they will also be included during assembly.

Example: To reference an item with linkId = "patient-name" from a Questionnaire located at http://example.org/Questionnaire/QuestionLibrary, the reference would be:

http://example.org/Questionnaire/QuestionLibrary#patient-name

Declaring Questionnaire Libraries

To indicate that a Questionnaire serves as a reusable library of items, it should follow the Questionnaire Modular Library profile, which requires the inclusion of a usageContext with the code workflow and a value indicating Question Library.

This usageContext helps tools and systems distinguish libraries from standard data entry forms. It is recommended to follow the pattern defined in the sdc-usagecontext-questionnaire-library profile.

Examples

This specification includes a simple set of examples that highlight the functionality of the two different modular questionnaire mechanisms. They also serve as test cases for systems that might want to check their support for the $assemble operation (or equivalent internal functionality). The files are as follows:

  • modular-root - A simple questionnaire with a couple of items of its own (some using definitions, some defined inline) as well as references to sub-questionnaires
  • modular-name - A re-useable questionnaire that can capture a person's name
  • modular-contact - A re-useable questionnaire that can capture a person's contacts
  • question-library - A library of re-useable questionnaire questions
  • modular-root-assembled - An assembled version of the modular-root questionnaire

Root Questionnaire (modular-root)

This is the Questionnaire that uses a mix of direct element definitions (via definition) and modular structure (via subQuestionnaire). It includes a launch context and a variable for linkIdPrefix to support nested item prefixing during assembly. The subQuestionnaire extension references a submodule Questionnaire that will be inserted during the $assemble operation.

Sub-Questionnaire (modular-name)

This is a reusable sub-questionnaire module that defines a structure for capturing a name. It includes three items: title, first name, and last name. The example demonstrates the use of definition to pull structure from a data library and initialExpression to prepopulate values from context. This module is designed to be referenced from a parent Questionnaire using subQuestionnaire.

Nested Module (modular-contact)

This module represents a patient's contact information and includes its own subQuestionnaire reference to the modular-name module. It demonstrates nesting of sub-questionnaires.