|
14 min de lectura
|

Bindings sin ataduras

Resumen del artículo

Los bindings adicionales —introducidos en FHIR R5— permiten asociar múltiples restricciones de Terminology a un único elemento, cada una con su propio propósito: exigir códigos específicos para datos nuevos, sugerir un subconjunto inicial, proporcionar sugerencias para la interfaz de usuario o aplicar reglas específicas de cada jurisdicción. Se superponen al binding principal y nunca lo reemplazan.

Resumir este artículo con:
ChatGPTPerplexityClaudeGrok

Un elemento, un binding — y sus limitaciones

Desde FHIR DSTU2, cada elemento codificado ha tenido exactamente un binding — una regla que vincula el elemento a un conjunto de códigos permitidos (un ValueSet) con una determinada fortaleza. La fortaleza indica con qué rigor deben seguirse los códigos: required significa que DEBERÁ utilizarse un código de ese conjunto; extensible significa que se debe usar uno si aplica al concepto en cuestión, aunque se pueden usar otros códigos; preferred y example son sugerencias progresivamente más laxas. Para elementos sencillos como Patient.gender (vinculado a AdministrativeGender con fortaleza required), este modelo funciona a la perfección.

Pero a medida que creció la adopción de FHIR — a través de jurisdicciones, dominios clínicos y marcos regulatorios — el modelo de un único binding alcanzó sus límites. Los autores de perfiles (personas que escriben StructureDefinitions — conjuntos de restricciones sobre los recursos FHIR base) se topaban repetidamente con problemas que un único ValueSet no podía expresar.

La saga de Extensible+Max

El primer recurso fue el patrón Extensible+Max. US Core necesitaba una manera de decir: «use códigos de este pequeño ValueSet, pero si debe extenderlo, permanezca dentro de este límite más amplio». La solución fue la extensión maxValueSet, introducida en STU3 y perfeccionada en R4.

De inmediato generó confusión. La especificación la describía como «un binding "required" sobre el binding extensible» — pero nadie se ponía de acuerdo sobre lo que eso significaba en la práctica. La tensión de fondo quedó al descubierto en un hilo de 182 mensajes en chat.fhir.org: Argonaut necesitaba «use este código o texto libre» para el cumplimiento de ONC — los hospitales tenían problemas con texto libre que era «literalmente una cadena en el sistema local» (Jenni Syed). Pero ninguna combinación de fortalezas de binding podía expresar eso. Josh Mandel señaló que US Core Immunization vinculaba vaccineCode a CVX con tanto extensible como max — al mismo ValueSet: «Desde una perspectiva de interoperabilidad, no entiendo por qué elegir extensible y máximo es mejor que simplemente elegir extensible».

Mientras tanto, una extensión minValueSet paralela intentó establecer un suelo para los bindings extensibles — si un código de ese conjunto aplica, debe usarse. Pero confundía las restricciones del emisor con las obligaciones del receptor, y los validadores no podían aplicarla.

Ambas extensiones solo podían expresar dos casos de uso muy concretos, y ni siquiera esos se comprendían bien.

El problema de «quiero múltiples bindings»

Al mismo tiempo, los autores de IG pedían constantemente múltiples bindings en un mismo elemento. El principal impulsor era IPS (International Patient Summary), que necesitaba expresar una elección de bindings de Terminology en un CodeableConcept — distintos sistemas de códigos para distintas jurisdicciones. La única herramienta disponible era el slicing — el mecanismo de FHIR para indicar que «este elemento repetido debe contener entradas que coincidan con patrones específicos». Aplicado a CodeableConcept.coding, el slicing permitía exigir un Coding de SNOMED y otro de ICD-10. Pero resultaba complejo de crear, difícil de entender para los implementadores y verboso en la representación de perfiles.

Como Grahame Grieve lo expresó: «Hay mucho slicing en las Implementation Guides porque los autores quieren decir "uno de los códigos debe provenir del value set X". Es el uso más frecuente del slicing, y hay que recurrir a él porque un binding se aplica a todas las repeticiones del código. Pero el slicing es difícil para los implementadores».

La propuesta de Grahame (febrero de 2022)

En febrero de 2022, Grahame Grieve propuso una nueva extensión sobre ElementDefinition.binding — bindings adicionales. La idea central: el binding principal se mantiene como binding de conformidad (lo que el validador aplica), pero se pueden adjuntar bindings adicionales con diferentes propósitos:

Me gustaría definir una extensión sobre ElementDefinition.binding que especifique bindings adicionales. ElementDefinition.binding es el binding de conformidad: lo que el validador aplica — y solo puede haber uno. Pero además, los diseñadores podrán especificar bindings adicionales.

Su lista inicial de propósitos — conformance, required, recommended, UI, starter, component, minimum, maximum — abordaba cada una de las carencias con las que la comunidad había estado lidiando. La motivación:

En algunos lugares usamos extensible+max y realmente queremos un value set base con un value set recomendado. En otros usamos extensible, y realmente queremos un value set base bastante amplio con un required — esto es lo que la legislación exige para los registros nuevos.

Rob Hausam vio de inmediato el valor para IPS: «Coincido en que parece que esto puede ser un sustituto del slicing en CodeableConcept.coding... Asumiendo que todo eso es cierto, ¿cuándo podemos empezar a usar esto en IPS?»

Lloyd McKenzie mostró escepticismo: «En general, si las reglas son diferentes para distintos contextos, se tienen diferentes perfiles». Pero Grahame respondió: «No le hace ningún favor a los usuarios tener una multitud de perfiles que solo difieren por el contexto de uso y algunos bindings».

En abril de 2022, el IG Publisher tenía soporte de renderizado en borrador, y Grahame añadió el indicador any — «si any = true, el binding solo aplica a una de las repeticiones, no a todas». En agosto de 2022, IPS ya lo estaba utilizando. La funcionalidad fue incorporada a la votación de R5 como ElementDefinition.binding.additional, y se publicó en R5 (marzo de 2023) como un elemento de primer nivel — ya no como una extensión.

Cómo funciona: la semántica primero

Antes de profundizar en el catálogo de propósitos, dos reglas fundamentales.

Regla 1: todos los bindings son AND. El binding principal y cada binding adicional se aplican simultáneamente. Lloyd McKenzie lo afirmó con claridad: «Todos los bindings se aplican siempre. Punto.» Sin embargo, no todos los bindings tienen el mismo peso:

  • Los bindings con un contexto usage se ignoran si no se está en ese contexto
  • Los propósitos de orientación (starter, ui, component, preferred) no tienen peso de conformidad
  • Los bindings current solo aplican a datos capturados desde la declaración de cumplimiento — no a datos heredados
  • Los bindings extensible tienen la salida estándar: no es obligatorio cumplirlos si ningún código del ValueSet aplica al concepto

Si se desea semántica OR («envíe un código del ValueSet A o del ValueSet B»), hay que combinarlos en un único ValueSet agrupado. Los bindings adicionales no proporcionan OR.

Para un CodeableConcept — el tipo de dato de FHIR que puede contener múltiples representaciones paralelas de códigos (Codings) del mismo concepto — múltiples bindings required pueden requerir múltiples Codings, uno que satisfaga cada binding. Así es como funciona «debe enviar SNOMED Y ICD-10» sin necesidad de slicing.

Regla 2: el indicador any cambia la semántica de las repeticiones. Por defecto, un binding se aplica a todas las repeticiones de un elemento. Establecer any = true significa que el binding se satisface si cualquier repetición coincide. Este es el mecanismo clave que reemplaza los slices de coding — y como señaló Lloyd McKenzie aquí: «usar slicing para bindings "and" se considera ahora un antipatrón y se desaconseja».

Estructura y propósitos

Un binding adicional nunca reemplaza al binding principal — se superpone a él. Cada uno tiene:

PropiedadDescripción
purposePor qué existe este binding (véanse las tablas a continuación)
valueSetLa URL del ValueSet
documentationMarkdown que explica el uso en contexto
shortDocoUn breve resumen en texto plano para la representación tabular
usageUsageContext delimitado por jurisdicción, dominio o flujo de trabajo
anySi es true, se satisface cuando cualquier repetición coincide (por defecto: todas deben coincidir)

Así es como aparece en una StructureDefinition (notación simplificada):

binding:
  strength: extensible
  valueSet: http://hl7.org/fhir/ValueSet/observation-codes
  additional:
    - purpose: starter
      valueSet: http://example.org/ValueSet/common-lab-codes
      documentation: >
        A manageable subset of ~200 common laboratory codes
        for implementers getting started.
    - purpose: current
      valueSet: http://example.org/ValueSet/2026-lab-codes
      documentation: >
        Required for all new observations captured after 2026-01-01.
        Legacy data may use codes outside this set.

Los propósitos han evolucionado desde la propuesta de Grahame en 2022 (algunos fueron renombrados, conformance fue eliminado). El elemento purpose tiene un binding required — solo se pueden utilizar códigos del ValueSet AdditionalBindingPurposeCodes; no se permiten propósitos personalizados. Estos son los códigos de R5:

Propósitos de conformidad

PropósitoSignificado
requiredEl elemento DEBERÁ usar un código de este ValueSet — en el contexto de uso especificado
extensibleUse códigos de este ValueSet si aplican — en el contexto de uso especificado
currentLos registros nuevos DEBERÁN usar este ValueSet. Los datos heredados o procedentes de fuentes externas pueden usar otros códigos

Propósitos de orientación

PropósitoSignificado
minimumTodas las aplicaciones conformes deben aceptar estos códigos como válidos (obligación de la aplicación, no restricción de la instancia)
candidatePuede sustituir al binding principal en situaciones documentadas (p. ej., una jurisdicción define un reemplazo)
preferredEste ValueSet es preferido en un contexto dado (la documentación debe explicar por qué)
uiCódigos sugeridos para la búsqueda y selección en la interfaz de usuario
starterUn subconjunto práctico con el que comenzar cuando el ValueSet completo es demasiado extenso
componentDocumenta cómo se usa una parte del ValueSet principal (p. ej., por sistema de códigos)

R6 añade current-extensible (semántica extensible para datos nuevos), best-practice (marcado en el modo de validación de buenas prácticas) y open (sin restricciones, para documentación).

El propósito maximum existe en R5 pero está obsoleto en R6. Originalmente estaba pensado para limitar los bindings extensibles, pero su semántica era poco clara (véase la saga de Extensible+Max más arriba). Use required con un contexto de uso en su lugar.

A través de las versiones de FHIR

VersiónFuncionamiento de los bindings adicionales
R4 y anterioresNo son nativos. Use la extensión additional-binding del Tooling Extensions IG. El IG Publisher los renderiza; el validador los admite. Las antiguas extensiones maxValueSet y minValueSet siguen funcionando, pero han quedado superadas.
R5Nativos como ElementDefinition.binding.additional. Soporte completo del validador. Sin elemento key — la restricción de bindings adicionales en perfiles derivados es limitada.
R6Nativos + elemento key para restringir entre capas de perfiles. Nuevos propósitos: current-extensible, best-practice, open. maximum obsoleto.

Para IGs en R4, los bindings adicionales se expresan como una extensión sobre ElementDefinition.binding:

binding:
  strength: extensible
  valueSet: http://hl7.org/fhir/ValueSet/observation-codes
  extension:
    - url: http://hl7.org/fhir/tools/StructureDefinition/additional-binding
      extension:
        - url: purpose
          valueCode: current
        - url: valueSet
          valueCanonical: http://example.org/ValueSet/2026-codes
        - url: documentation
          valueMarkdown: Required for new records.

En R5+, lo mismo se expresa de forma nativa — sin necesidad de contenedor de extensión.

Escenarios

Conformidad en múltiples jurisdicciones

Un perfil internacional necesita distintos sistemas de códigos en distintos países — SNOMED CT en EE. UU., ICD-10-GM en Alemania. Antes de los bindings adicionales, esto implicaba perfiles separados por jurisdicción o un slicing complejo. Ahora puede expresarse en un único perfil usando required + any: true + usage:

binding:
  strength: extensible
  valueSet: http://hl7.org/fhir/ValueSet/condition-code
  additional:
    - purpose: required
      valueSet: http://example.org/ValueSet/us-snomed-conditions
      any: true
      usage:
        - code: { code: US, system: urn:iso:std:iso:3166 }
      documentation: US jurisdiction — at least one SNOMED CT coding required.
    - purpose: required
      valueSet: http://example.org/ValueSet/de-icd10gm-conditions
      any: true
      usage:
        - code: { code: DE, system: urn:iso:std:iso:3166 }
      documentation: German jurisdiction — at least one ICD-10-GM coding required.

Tres características trabajando juntas: required hace que el binding sea aplicable, any: true significa «al menos un Coding debe coincidir» (no todos — por lo que se pueden seguir enviando otros codings), y usage delimita cada binding a su jurisdicción. Un sistema estadounidense solo ve el requisito de SNOMED; un sistema alemán solo ve ICD-10-GM. El binding extensible principal se aplica a ambos.

Migración de datos heredados

Un problema habitual: un país hace la transición a un nuevo estándar de codificación, pero los hospitales tienen décadas de datos con el antiguo. Sin bindings adicionales habría que elegir — o el binding es lo suficientemente flexible para los datos heredados (y se pierde la conformidad para los datos nuevos) o es estricto para los datos nuevos (y los registros antiguos no superan la validación).

Los bindings adicionales resuelven esto con current + minimum:

binding:
  strength: extensible
  valueSet: http://hl7.org/fhir/ValueSet/condition-code
  additional:
    - purpose: current
      valueSet: http://example.org/ValueSet/icd-11-conditions
      documentation: >
        All new diagnoses captured after 2026-01-01 must use ICD-11.
    - purpose: minimum
      valueSet: http://example.org/ValueSet/icd-10-icd-11-conditions
      documentation: >
        Systems must accept both ICD-10 and ICD-11 codes.

El binding principal sigue siendo amplio (condition-code — extensible). El binding current indica: «a partir de ahora, use ICD-11». El binding minimum indica: «pero todos los sistemas conformes deben seguir aceptando ICD-10 en los datos entrantes». Ninguno reemplaza al binding principal — añaden precisión donde el modelo anterior solo tenía un único parámetro que ajustar.

Bindings dependientes de la categoría (SDOH)

Este es un patrón real de los perfiles US Core SDOH. Condition.code está vinculado a un ValueSet amplio, pero las distintas categorías obtienen bindings más estrictos — si la categoría es food-insecurity, el código debe provenir del VS de Diagnósticos de Inseguridad Alimentaria SDOH; si es housing-instability, de un VS diferente. Cada binding adicional usa usage para delimitar por valor de categoría:

# Condition.code
binding:
  strength: required
  valueSet: http://hl7.org/fhir/us/core/ValueSet/us-core-condition-code
  additional:
    - purpose: required
      valueSet: http://hl7.org/fhir/us/sdoh/ValueSet/food-insecurity-diagnoses
      usage:
        - code: { code: food-insecurity, system: http://hl7.org/fhir/us/sdoh/CodeSystem/sdoh-category }
      documentation: >
        When Condition.category = food-insecurity,
        code must come from this ValueSet.
    - purpose: required
      valueSet: http://hl7.org/fhir/us/sdoh/ValueSet/housing-instability-diagnoses
      usage:
        - code: { code: housing-instability, system: http://hl7.org/fhir/us/sdoh/CodeSystem/sdoh-category }
      documentation: >
        When Condition.category = housing-instability,
        code must come from this ValueSet.

El contexto usage aquí no es la jurisdicción — es una categoría clínica dentro del mismo recurso. El validador comprueba: si el Condition tiene category = food-insecurity, entonces aplica el binding adicional de inseguridad alimentaria y code debe provenir de ese ValueSet específico. Si la categoría no coincide, el binding se ignora. Como Grahame confirmó: «si su categoría es una de las categorías designadas, entonces aplica el value set de esa categoría».

Sugerencias de interfaz para ValueSets extensos

No todos los bindings adicionales tratan sobre la conformidad. Los propósitos ui y starter no tienen peso de validación — ayudan a los implementadores a construir mejores interfaces. Un perfil de signos vitales puede tener más de 100 000 códigos LOINC en su binding extensible, pero la interfaz solo necesita unos pocos:

# Observation.code (vital signs)
binding:
  strength: extensible
  valueSet: http://hl7.org/fhir/ValueSet/observation-vitalsignresult
  additional:
    - purpose: starter
      valueSet: http://example.org/ValueSet/common-vitals
      documentation: >
        15 most common vital sign codes. Start here
        if you're building a new system.
    - purpose: ui
      valueSet: http://example.org/ValueSet/vitals-picker
      documentation: >
        Curated list optimized for dropdown selection.
        Not a conformance constraint.

El propósito starter indica «comience su implementación con estos códigos». El propósito ui indica «muestre estos códigos en un selector». Ninguno afecta a la validación — un sistema puede enviar cualquier código válido según el binding extensible principal. Pero resuelven un problema real: implementadores que se enfrentan a un ValueSet de 100 000 códigos sin saber por dónde empezar.

Sigue evolucionando

El mecanismo está maduro, pero la comunidad sigue resolviendo casos límite. Tan recientemente como a finales de 2025, Eric Haas escribió: «Discutimos este tema en el WGM y, como perfilador, sigo sin entender con claridad cómo se supone que deben funcionar los bindings adicionales». Él y Gay Dolin están creando una matriz de casos de uso explícitos con ejemplos para el FHIR Guidance IG. Áreas clave que se están refinando:

  • Bindings condicionales — usar usage (UsageContext) para delimitar bindings por jurisdicción o flujo de trabajo es poderoso pero complejo en la práctica (66 mensajes y contando).
  • Comportamiento del validador — la forma en que se notifican las infracciones varía según el propósito: required produce errores, los propósitos de orientación como starter y ui son solo informativos.

Referencias y debates de la comunidad

Especificación:

Comunidad (chat.fhir.org):

Véase también: Categorías residuales en FHIR Terminology.

Compartir este artículo
Comments
Comments
Sign in
Loading comments...
Subscribe to our blog

Get the latest articles on FHIR, interoperability, and healthcare IT.