Pair programming kata para el proceso de selección de Buchinger.
Nos interesa observar:
- cómo estructuras la información
- cómo combinas datos de distintas fuentes
- cómo decides qué forma parte del análisis
- cómo manejas inconsistencias
- cómo evoluciona tu diseño
- cómo explicas decisiones
- No necesitas terminar todo
- Puedes simplificar lo que consideres
- Puedes refactorizar en cualquier momento
- Puedes hacer preguntas
En Buchinger, los pacientes siguen tratamientos terapéuticos supervisados por un equipo médico.
Durante el tratamiento, los médicos necesitan entender el estado clínico del paciente en cada momento. Para ello, utilizan una herramienta interna que les muestra:
- resultados de laboratorio
- observaciones clínicas diarias
- señales relevantes para la toma de decisiones
Estos datos no provienen de un único sitio. Llegan desde distintos sistemas externos y en distintos momentos.
El sistema interno debe construir una visión clara, consistente y útil para el equipo médico.
Construir una pequeña parte de este sistema que permita:
- trabajar con datos clínicos de distintas fuentes
- generar un análisis clínico actual de un paciente
- entender la evolución durante el tratamiento
- manejar situaciones imperfectas (datos incompletos, correcciones, cambios)
┌──────────────────────────┐
│ Médico / Equipo │
│ (HTTP client) │
└────────────┬─────────────┘
│
▼
┌─────────────────────────────────────┐
│ clinical-analysis (puerto 3000) │ ◀── aquí trabajas
│ │
│ GET /patients/:id/clinical- │
│ analysis/current │
│ GET /patients/:id/clinical- │
│ analysis/timeline │
│ │
│ Arquitectura hexagonal + DDD │
│ TypeScript + Express + Prisma │
└──────┬──────────────────┬───────────┘
│ │ ┌───────────┐
▼ ▼ │ Postgres │
┌───────────────────┐ ┌──────────────────┐│ :5432 │
│ laboratory-api │ │ observations-api ││ │
│ (puerto 3001) │ │ (puerto 3002) │└─────┬─────┘
│ │ │ │ │
│ Fuente 1 — │ │ Fuente 2 — │◀─────┘ (Prisma)
│ Laboratory │ │ Treatment │
│ Results │ │ Observations │
└───────────────────┘ └──────────────────┘
ya implementada ya implementada
apps/clinical-analysis— el servicio interno donde trabajarás. Tiene scaffolding con arquitectura hexagonal + DDD,Express,Prismaconectado aPostgresy clientes HTTP para las dos fuentes externas. Los endpoints del enunciado no están implementados: ese es tu trabajo.apps/laboratory-api— servicio externo ya implementado que expone la Fuente 1. No necesitas tocarlo.apps/observations-api— servicio externo ya implementado que expone la Fuente 2. No necesitas tocarlo.postgres— base de datos disponible para que la uses como consideres (cache, histórico, señales, etc.).
Requisitos: Docker y Docker Compose.
docker-compose up --buildEso levanta:
| Servicio | URL | Descripción |
|---|---|---|
clinical-analysis |
http://localhost:3000 | El servicio donde trabajas |
laboratory-api |
http://localhost:3001 | Fuente 1 (Laboratory Results) |
observations-api |
http://localhost:3002 | Fuente 2 (Treatment Observations) |
postgres |
localhost:5432 |
kata / kata / clinical_analysis |
Comprobación rápida:
curl http://localhost:3000/health
curl http://localhost:3001/health
curl http://localhost:3002/health💡 Una colección completa de comandos
curlpara explorar las fuentes y cada escenario por paciente está endocs/exploring.md.
Las tienes ya montadas. No necesitas implementarlas, solo consumirlas.
📡 Fuente 1 — Laboratory Results (http://localhost:3001)
GET /v1/laboratory-results?patientRef={patientRef}[&treatmentRef={treatmentRef}]Devuelve una lista de informes de laboratorio:
[
{
"patient_ref": "PAT-001",
"treatment_ref": "TR-2026-001",
"sample_taken_at": "2026-05-12T08:30:00Z",
"results": {
"glucose_mg_dl": 68,
"ketones_mmol_l": 1.2,
"crp_mg_l": 4.8,
"sodium_mmol_l": 139
},
"reported_at": "2026-05-12T10:15:00Z"
}
]La v2 de esta fuente también existe (
/v2/laboratory-results). La usarás en la Parte 6.
📡 Fuente 2 — Treatment Observations (http://localhost:3002)
GET /observations?patientId={patientId}[&treatmentId={treatmentId}]Devuelve una lista de observaciones:
[
{
"patient_id": "PAT-001",
"treatment_id": "TR-2026-001",
"observed_at": "2026-05-12T09:00:00Z",
"vitals": {
"systolic_bp": 96,
"diastolic_bp": 61,
"heart_rate_bpm": 88
},
"symptoms": ["DIZZINESS", "FATIGUE"],
"fluid_intake_ml": 1200
}
]Todos los pacientes tienen datos suficientes para trabajar. Cada uno cubre un escenario distinto:
| Patient ID | Treatment ID | Escenario útil para... |
|---|---|---|
PAT-001 |
TR-2026-001 |
caso normal, varios puntos en el tiempo (Parte 1, 2, 3) |
PAT-002 |
TR-2026-002 |
CLINICAL_RISK_REVIEW_REQUIRED (hipoglucemia + mareo) |
PAT-003 |
TR-2026-003 |
FASTING_EVOLUTION_EXPECTED (cetosis + glucosa normal) |
PAT-004 |
TR-2026-004 |
solo laboratorio, sin observaciones (Parte 4) |
PAT-005 |
TR-2026-005 |
solo observaciones, sin laboratorio (Parte 4) |
PAT-006 |
TR-2026-006 |
contiene correcciones de resultados previos (Parte 5) |
Construir una forma de obtener el estado clínico actual de un paciente combinando ambas fuentes.
GET /patients/{patientId}/clinical-analysis/current- últimos datos de laboratorio relevantes
- últimas observaciones disponibles
- una vista unificada del estado del paciente
El formato de la respuesta es libre.
- Los datos vienen de dos fuentes distintas
- Pueden tener timestamps diferentes
- No siempre llegan al mismo tiempo
Añadir señales clínicas basadas en reglas de negocio.
Una lista de clinicalSignals dentro del análisis actual.
1. Glucosa baja
glucose < 70 mg/dL
→ LOW_GLUCOSE
2. Cetosis presente
ketones >= 0.5 mmol/L
→ KETOSIS_PRESENT
3. Hipotensión
systolic < 90 OR diastolic < 60
→ LOW_BLOOD_PRESSURE
4. Posible deshidratación
fluidIntake < 1500 ml
AND symptoms contiene DIZZINESS
→ POSSIBLE_DEHYDRATION
5. Riesgo clínico
LOW_GLUCOSE
AND symptoms contiene DIZZINESS
→ CLINICAL_RISK_REVIEW_REQUIRED
6. Evolución esperada del ayuno
KETOSIS_PRESENT
AND glucose entre 70 y 100
AND NO hay riesgo clínico
→ FASTING_EVOLUTION_EXPECTED
Permitir ver cómo evoluciona el paciente durante el tratamiento.
GET /patients/{patientId}/clinical-analysis/timeline- análisis en orden temporal
- datos disponibles en cada momento
- señales clínicas en cada punto
- Los datos pueden venir en momentos distintos
- No siempre hay laboratorio y observación al mismo tiempo
El sistema debe comportarse de forma razonable cuando falta información.
- solo hay laboratorio
- solo hay observaciones
- timestamps no coinciden
- una fuente no responde
- qué mostrar
- cómo evitar errores
- cómo mantener una experiencia útil para el médico
Gestionar cambios en datos ya recibidos.
El laboratorio corrige un resultado previo (mismo sample_taken_at, distinto reported_at):
{
"sample_taken_at": "2026-05-12T08:30:00Z",
"results": { "glucose_mg_dl": 74 },
"reported_at": "2026-05-12T12:45:00Z"
}El paciente PAT-006 ya tiene este tipo de correcciones en la fuente.
- qué dato se considera el actual
- qué pasa con el histórico
- qué pasa con señales ya calculadas
La fuente de laboratorio cambia su formato. La nueva versión está disponible en:
GET /v2/laboratory-results?patientId={patientId}[&treatmentId={treatmentId}]Devuelve algo como:
{
"patientId": "PAT-001",
"treatmentId": "TR-2026-001",
"sample": { "takenAt": "2026-05-12T08:30:00Z" },
"biomarkers": [
{ "code": "GLUCOSE", "value": 740, "unit": "mg/L" },
{ "code": "KETONES", "value": 1.2, "unit": "mmol/L" }
],
"reportedAt": "2026-05-12T12:45:00Z"
}Adaptar tu sistema para que el análisis clínico siga funcionando.
# Levantar todo
docker-compose up --build
# Solo la base de datos + las fuentes (para correr clinical-analysis en local con hot reload)
docker-compose up postgres laboratory-api observations-api
# Dentro de apps/clinical-analysis, en local:
cd apps/clinical-analysis
npm install
npm run prisma:generate # genera el cliente Prisma
npm run prisma:migrate # (opcional) crea tablas si añades modelos
npm run dev # arranca en modo watchLa estructura de apps/clinical-analysis sigue arquitectura hexagonal:
apps/clinical-analysis/src/
├── domain/ ← reglas y entidades puras del dominio (sin framework)
├── application/ ← casos de uso y puertos (interfaces)
│ └── ports/ ← LaboratoryResultsPort, TreatmentObservationsPort
└── infrastructure/ ← adaptadores, HTTP, persistencia, config
├── adapters/ ← clientes a las fuentes externas
├── http/ ← Express routes & controllers
└── persistence/ ← Prisma client
Los puertos a las fuentes externas ya están definidos; los adaptadores HTTP ya están implementados. Puedes inyectarlos en tus casos de uso directamente.
Uso interno de Code Sherpas para el proceso de selección de Buchinger.