Datos clínicos sin candado: de PMS dentales legacy a FHIR R4 en AWS HealthLake
Contexto
Los datos clínicos de un consultorio dental en México viven presos en su PMS (practice management system, el software de gestión del consultorio): bases de datos locales, formatos propietarios, cero interoperabilidad. Si una cadena de clínicas quiere ver su operación completa — o un dentista quiere cambiar de software sin perder diez años de expedientes — no hay puerta de salida.
Integra Dental Bridge es nuestra respuesta: una plataforma que extrae los datos del PMS que sea, los convierte al estándar internacional FHIR R4 (el formato universal de datos de salud, el mismo que usan hospitales y aseguradoras) y los materializa en un almacén clínico gestionado en AWS. Este paper documenta las decisiones de arquitectura.
Problema
Tres restricciones definieron el diseño:
- Los PMS son legacy y on-premise. El software corre en la computadora del consultorio, con bases de datos locales (nuestro conector de referencia es OpenDental; el segundo, Dentalink). No hay APIs modernas ni webhooks: hay que extraer, detectar cambios y sincronizar sin romper la operación de la clínica.
- Cada PMS habla su propio idioma. El mismo concepto — paciente, cita, tratamiento — tiene estructura distinta en cada sistema. El mapeo no puede estar quemado en código: agregar un PMS nuevo no debe requerir reescribir la plataforma.
- Son datos de salud. Trazabilidad completa, secretos fuera del código, y un modelo canónico auditable no son features: son requisitos de entrada.
Arquitectura

Diagrama generado desde código (diagramas/generate_dental_bridge.py) con la librería diagrams + Graphviz — cada flecha entre servicios es un salto por el bus de eventos.
- Agente de sincronización on-premise: un servicio Windows instalable en la clínica detecta cambios en la base del PMS y los emite como eventos. La clínica no cambia nada de su operación.
- Kafka como columna vertebral (MSK/Confluent, eventos Avro con Schema Registry): los servicios nunca se llaman entre sí — toda comunicación es un evento tipado en el namespace
com.integradental.*, contrato compartido entre los dos monorepos (Python y TypeScript). - Motor de mapeo JSONata: las transformaciones PMS→canónico son datos, no código — expresiones JSONata versionadas que un humano puede revisar y un servicio de mapeo interactivo permite construir. Agregar un PMS es escribir mapeos, no reescribir servicios.
- AWS HealthLake como almacén clínico: FHIR R4 gestionado, con el data plane firmado SigV4 y jobs masivos de import/export. No operamos el FHIR server — AWS lo hace.
- Temporal para flujos largos: sincronizaciones completas, importaciones masivas y reportes de auditoría corren como workflows durables que sobreviven reinicios y reintentos.
- API multi-tenant (NestJS + Prisma): organizaciones → cuentas → marcas → consultorios; el dashboard React opera la plataforma.
- Snowflake como almacén analítico downstream, alimentado desde el mismo bus de eventos por un sink dedicado.
Decisiones
- Eventos vs llamadas directas: elegimos que todo pase por Kafka. Trade-off: más piezas móviles y una curva de operación mayor, a cambio de servicios desacoplados que escalan y fallan por separado — y un solo lugar (el bus) donde auditar todo lo que pasó. Con datos clínicos, esa auditoría vale el costo.
- JSONata vs mapeo en código: las transformaciones viven como expresiones versionadas, no como Python. Trade-off: un motor más a mantener, a cambio de que el conocimiento del dominio dental sea legible, revisable y reutilizable por PMS. Incluye un paquete de mapeo asistido por LLM (
bridge_llm) para acelerar el borrador inicial de mapeos nuevos. - HealthLake vs FHIR server propio (HAPI): gestionado. Trade-off: menos control fino y costo por hora, a cambio de cero operación de un componente crítico regulado. Para un equipo chico, operar un FHIR server propio es una distracción que no paga.
- Dos monorepos (Python y TypeScript) con un contrato compartido: los conectores y el mapeo viven en Python (el ecosistema de datos), la API multi-tenant y los workflows en TypeScript (NestJS + Temporal). El contrato son los esquemas Avro — si compila contra el Schema Registry, los dos mundos se entienden.
- Multi-tenant desde el día uno: la jerarquía organización → cuenta → marca → consultorio está en el modelo de datos desde el inicio. Trade-off: complejidad temprana, a cambio de que una cadena de 40 clínicas y un consultorio individual corran sobre la misma plataforma.
Resultados
| Métrica | Valor | Fuente |
|---|---|---|
| Paquetes Python | 29 (conectores, mapeo, Kafka, cloud, CLI) | monorepo py |
| Paquetes TypeScript | 13 (API, dashboard, Temporal, esquemas Kafka) | monorepo ts |
| Conectores PMS | 2 (OpenDental como patrón de referencia, Dentalink) | packages/ |
| Modelo canónico | FHIR R4 completo vía AWS HealthLake | bridge_fhir, bridge_cloud |
| Infra AWS | HealthLake, MSK, S3, RDS Postgres, Secrets Manager | cuenta 041876399045 |
| Python | 3.13 estricto, workspace uv | pyproject.toml |
| Comunicación entre servicios | 100% eventos (cero llamadas directas) | topics.py, namespace com.integradental |
Lecciones
- El contrato es el esquema, no la API. Compartir esquemas Avro entre dos monorepos de lenguajes distintos resultó más robusto que cualquier documentación: si el esquema no compila, el error sale en build, no en producción.
- Mapeo como datos cambia la economía de los conectores. El segundo conector (Dentalink) reutilizó el patrón completo del primero; el costo marginal de un PMS nuevo es escribir sus mapeos JSONata.
- Gestionado gana en dominios regulados. HealthLake nos quitó de encima la operación del componente más delicado (el FHIR store) — la energía se fue a los conectores, que es donde está el valor diferencial.
- Renombrar temprano. El prefijo heredado
hd_se migró abridge_en julio 2026 en 29 paquetes; hacerlo antes de tener más consumidores downstream fue barato. Seis meses después habría sido una cirugía. - Lo pendiente honesto: los conectores para los PMS dominantes en México se construyen sobre el patrón de referencia — el paper de esa expansión, con números de clínicas reales, será la secuela.