Modelo de datos
El modelo de datos de EMA Lab refleja la dualidad arquitectónica del producto. Lo clínico canónico —observaciones, reportes, especímenes, diagnósticos microbiológicos, antibiogramas— vive como recursos FHIR R4 en Aidbox con perfiles del CL Core y del EMA IG cuando aplican. Lo operacional —catálogos, configuración, QC, cultivos en seguimiento, inventario, conectores, no-conformidades, stewardship, billing— vive en Supabase con RLS estricta por tenant. La división es deliberada: forzar todo el estado a FHIR habría sido contraproducente para conceptos que el estándar no modela bien (workflows operacionales, configuración de equipos, retry queues), y forzar todo a Supabase habría sacrificado la portabilidad clínica.
Schemas Supabase
Sección titulada «Schemas Supabase»EMA Lab opera con dos schemas dentro del proyecto emahub
(bzikofdvfvchntvfzyni). El schema emashared agrupa lo
transversal al ecosistema —tenants, users, configuración EMI,
credenciales encriptadas de Aidbox—, y lo escriben tanto Vault
durante el provisioning como cada producto cuando registra cambios.
El schema emalab contiene las 62 tablas operacionales propias del
LIS, todas con columna tenant_id y políticas RLS que aplican
tenant_id = (select tenant_id from user_roles where user_id = auth.uid() and product = 'lab').
emashared (compartido cross-product): tenants # uuid id, slug, country, status tenant_products # tenants × products activos tenant_aidbox_credentials # AES-GCM encrypted OAuth (S9) tenant_emi_access # is_active, rate-limit tenant_slug_history # rename audit emi_apps # apps × tenant (lab='alpha' hasta Wave 5) users # auth.users mirror + perfil user_roles # user × tenant × product × role user_client_access # filtro adicional para roles client-isolation client_config # client externos (ej. clínicas que envían órdenes) workplace_config # config por sede
emalab (62 tablas — Wave 3 v2.0):
Catálogo y profiles: test_catalog # 50 tests LOINC seed, per-tenant specimen_types # 6 (sangre, orina, etc.) containers # 11 tipos de tubo / contenedor lab_profiles # 5 perfiles (perfil bioquímico, hematológico, etc.) lab_config # whitelabel, billing, thresholds, auto_consume_enabled barcode_strategy # estrategia per-tenant Code 128B (L010) label_templates # plantillas etiqueta tubo specimen_label_map # mapping specimen → template
Órdenes y muestras: routing_rules # reglas inter-sede (ADR-006) transport_routes # rutas físicas lab_sites # sedes del tenant exclusions # tests excluidos por cliente
QC: qc_lots # lots reactivos QC qc_lot_targets # mean/SD por lot qc_config # Westgard rules per-tenant breakpoint_config # antibiograma breakpoints
Microbiología (Fase 4 + S4): culture_tracking # 7 stages (ADR-020) micro_organism_catalog # 80+ SNOMED seed (L052) antibiotic_catalog # antibióticos antibiogram_panels # 3 paneles seed micro_config # timeouts, qualitative thresholds
Inventario: reagent_catalog # reactivos master reagent_inventory # lots con FIFO reagent_consumption # historial consumo
Validación y autoverify: validation_audit # cada decisión de cada stage autoverify_conditions # reglas autoverify per-test emi_rules # reglas EMI per-tenant emi_section_config # config EMI por sección tech_section_auth # auth TM por sección (L028)
Critical values: critical_value_log # log con communication ack critical_value_source # fuente del trigger (L047)
Billing y DTE: billing_config # 7 cols DTE per-tenant (L026) price_lists # listas precio per-cliente test_prices # precio per-test per-list invoices, invoice_lines # facturación
Conectores: connector_subscriptions # FHIR Subs activos connector_hl7v2_config # endpoints HL7v2 per-cliente connector_log # log con duration_ms (L046) connector_retry_queue # retry FHIR + HL7v2 outbound (L048) hub_registry # tokens Hub on-prem dlq_alert_log # Loops DLQ alerts con debounce 15min
Molecular (S5): molecular_imports # cada importación parsed molecular_critical_targets # targets críticos admin molecular_review_entries # pending review queue
Stewardship (S5/S6): stewardship_alerts # alertas generadas stewardship_alert_config # config trimestral stewardship_signers # multi-signer reorder/toggle (L075) stewardship_reports # reportes generados stewardship_templates # templates print
Equipos y mantenimiento: maintenance_plan # plan preventivo maintenance_history # ejecuciones equipment_documents # certificados, manuales sla_config # SLA per-cliente per-sección
No conformidades: nonconformities # CLSI GP41 (16 razones, ADR-008) detected_issue_emission # status emisión FHIR
Patient sequence: patient_sequence # generador identifier MR per-tenantEl estado actual de migraciones aplicadas es 001 a 022, con la
migración 010 deprecada y descartada. Todas están aplicadas en el
hub de producción bzikofdvfvchntvfzyni (decisión L103). Las
políticas RLS sobre cada tabla con tenant_id aplican
tenant_id = (select tenant_id from user_roles where user_id = auth.uid() and product = 'lab'),
lo que aísla cada laboratorio del resto y, dentro del ecosistema,
distingue entre productos (un mismo usuario puede tener roles en
Lab y Clinic con tenants distintos sin que las queries se mezclen).
Diagrama ER (emalab core)
Sección titulada «Diagrama ER (emalab core)»Recursos FHIR utilizados
Sección titulada «Recursos FHIR utilizados»EMA Lab trabaja con 29 recursos FHIR R4 distintos, cada uno con un
perfil aplicado del CL Core o del EMA IG según corresponda. El SDK
emafhir versión 0.6.0 se encarga de inyectar
meta.tag = urn:ema:tenant|<tenantId> en cada recurso al crearlo o
actualizarlo, lo que sostiene una de las capas del aislamiento. La
adopción del helper createEmaClient es opt-in por handler
(decisión L102), lo que permite migrar progresivamente sin romper
los handlers que todavía usan el patrón anterior. Los value sets
canónicos (LOINC para tests, SNOMED para microorganismos y
patologías, ICD-10 para diagnósticos) viven en inglés según los
emite cada organismo y se presentan con display localizado al
idioma del tenant.
Clínicos core
Sección titulada «Clínicos core»| Recurso | Notas |
|---|---|
Patient | identifier MR per-tenant (sequence con Luhn), demografía e identificador nacional según país |
ServiceRequest | Per test (ADR-003), no per profile. code LOINC, subject, requester, performer, basedOn |
Specimen | subject, type, collection, container, accessionIdentifier |
Encounter | Heredado del workplace (ADR-004) — para LIS no se crea uno nuevo |
Observation | code LOINC, value, referenceRange, interpretation, status (registered → preliminary → final → amended) |
DiagnosticReport | code, subject, result[], presentedForm (PDF), status (partial → preliminary → final → amended) |
Condition | Para microbiología — diagnóstico microbiológico |
AllergyIntolerance | Read-only desde EHR cliente |
Microbiología
Sección titulada «Microbiología»| Recurso | Notas |
|---|---|
DiagnosticReport (micro) | partial → preliminary → final con cascada Observation |
Observation (culture) | parent observation con category=culture |
Observation (organism) | child con code SNOMED de organismo identificado |
Observation (panel) | child con SIR + MIC/zone como component[] (v3-ObservationInterpretation) |
Task (critical) | critical-value-notification para hemocultivo+ (L051) |
Order management
Sección titulada «Order management»| Recurso | Notas |
|---|---|
Task | Workflow tasks — Hub pull, retry, etc. |
Communication | Critical value notification con ack del MD |
DocumentReference | Reportes en R2 (DOCS_BUCKET), categorías clinical-note/lab-report/stewardship/molecular |
DetectedIssue | Stewardship alerts canonical FHIR (L071) |
Validación y QC
Sección titulada «Validación y QC»| Recurso | Notas |
|---|---|
Observation (QC) | category=qc, value comparado contra qc_lot_targets (Westgard 7-T L024) |
Provenance | Quién firmó qué — multi-signer stewardship (L075) |
AuditEvent | Inmutable — todo log clínico |
State machines
Sección titulada «State machines»ServiceRequest (Lab Order)
Sección titulada «ServiceRequest (Lab Order)»Specimen
Sección titulada «Specimen»DiagnosticReport
Sección titulada «DiagnosticReport»Culture tracking (microbiología)
Sección titulada «Culture tracking (microbiología)»Validation pipeline (4 stages)
Sección titulada «Validation pipeline (4 stages)»Stewardship report (CLSI M39)
Sección titulada «Stewardship report (CLSI M39)»Patrones de modelado
Sección titulada «Patrones de modelado»Per-test ServiceRequest (ADR-003)
Sección titulada «Per-test ServiceRequest (ADR-003)»Cada test es una ServiceRequest, no agrupados por profile.
Razón: facturación, routing, validación y reportes son granular por
test. Profiles son una agrupación visual en lab_profiles que
expanden a múltiples ServiceRequests al crear orden.
Encounter heredado (ADR-004)
Sección titulada «Encounter heredado (ADR-004)»EMA Lab no crea Encounters propios — hereda el Encounter del
EHR que originó la orden (típicamente emaclinic). Si la orden viene
sin Encounter, se asocia a workplace_config.default_encounter.
Barcode sin site prefix (ADR-005)
Sección titulada «Barcode sin site prefix (ADR-005)»Code 128B con check digit Luhn — formato <8-char-ID><Luhn> sin
prefijo de sede. Razón: muestras se mueven entre sedes (routing
inter-sede) y un prefijo fijo crearía conflictos.
Critical value via Communication (ADR-014)
Sección titulada «Critical value via Communication (ADR-014)»Comunicación de valores críticos modelada como Communication con
ack del MD receptor — no como flag silenciosa. Permite trazar
plazo de notificación regulatorio (Ley 20.584) + audit completo.
Non-conformity como DetectedIssue (ADR-008)
Sección titulada «Non-conformity como DetectedIssue (ADR-008)»Las no-conformidades CLSI GP41 (16 razones) se modelan como
DetectedIssue FHIR — permite query cross-product y agregación en
reportes regulatorios.
Stewardship dual-write (L071)
Sección titulada «Stewardship dual-write (L071)»CLSI M39 alerts viven en Supabase stewardship_alerts (primary,
operacional) + se emiten como DetectedIssue FHIR (canonical). 3
columnas en Supabase tracking emisión:
fhir_emission_status / _error / _at. Retry sweep cron */15min
re-emite pendientes (L079).