Arquitectura y Seguridad
Este documento detalla la estructura técnica del ecosistema de Salud y las capas de protección implementadas para garantizar la integridad de los datos (PII/PHI) y el cumplimiento legal.
1. Stack Tecnológico Oficial
| Capa | Tecnología | Justificación Técnica |
|---|---|---|
| Backend Core | Go (Golang) | Alta performance, gestión eficiente de concurrencia (Goroutines) para búsquedas masivas y binarios ligeros para microservicios. |
| Frontend Web | Next.js (React + TS) | Renderizado híbrido (SSR/SSG) para SEO médico, tipado fuerte con TypeScript y excelente manejo de rutas. |
| Mobile App | Flutter | Código único para iOS y Android con performance nativa y una interfaz de usuario altamente consistente. |
| Base de Datos | PostgreSQL + PostGIS | Estándar de oro para datos relacionales y consultas geográficas complejas (Validación Triple). |
| Comunicación | gRPC / REST | gRPC para comunicación ultra rápida entre servicios internos y REST/JSON para el Frontend. |
| Caché / Colas | Redis | Alta velocidad para sesiones y gestión de envíos de correo en background. |
| Documentación | Docusaurus + Swagger | Living Documentation integrada al ciclo de vida. |
2. Arquitectura del Sistema (C4 Model)
2.1 Nivel 1: Diagrama de Contexto
Muestra cómo el sistema interactúa con los actores externos y servicios de terceros.
2.2 Nivel 2: Diagrama de Contenedores
Detalle de la infraestructura interna y la comunicación entre servicios.
2.3 Patrón DDD + Clean Architecture (Go)
- Capas:
domain(entidades/valores),application(use cases),infrastructure(adapters: repos DB, pubsub, http clients),interfaces/http(handlers/DTOs). Dependencias solo hacia adentro;domainno importa infraestructura. - Reglas: handlers solo orquestan y mapean DTO ↔ use case; validaciones de negocio viven en
application; repos son interfaces definidas en dominio/aplicación y sus implementaciones eninfrastructure/persistence. - Carpetas sugeridas (por servicio Go):
/cmd/<service>(wire DI),/internal/domain/<bounded_context>,/internal/application/<usecase>,/internal/infrastructure/{persistence,pubsub,clients},/internal/interfaces/http(handlers, middleware, mappers). - DTOs vs entidades: los contratos HTTP (OpenAPI) modelan DTOs de interfaz; no exponen detalles de persistencia ni structs de dominio.
3. Infraestructura y Gestión de Datos
3.1 Gestión de Secretos (Security First)
Se prohíbe el almacenamiento de credenciales en el código fuente o variables de entorno del servidor sin cifrar.
- Herramienta: Uso de [Secret Manager - ej. HashiCorp Vault / AWS Secrets] para inyección dinámica de secretos en tiempo de ejecución.
- Zero Trust: Cada microservicio en Go tiene permisos mínimos (IAM roles) para acceder solo a sus tablas y buckets específicos.
3.2 Almacenamiento de Objetos (Storage)
- Servicio: S3 compatible (AWS S3 / Google Cloud Storage).
- CDN: Cloudflare para el cacheo de activos estáticos (fotos de médicos, logos).
- Procesamiento: Uso de un Worker en Go para el redimensionamiento y compresión de imágenes a formato WebP tras la carga (REG-600/700).
4. Estrategia de Seguridad y Autenticación
Para mitigar riesgos de secuestro de sesión y ataques de fuerza bruta, se implementa el siguiente estándar:
4.1 Gestión de Tokens (Security Stack)
- JWT Dinámico: El Access Token emitido por el servicio en Go tendrá una validez de 15 minutos.
- Almacenamiento Seguro:
- Web: El Refresh Token se almacena en una Cookie HttpOnly, Secure y SameSite=Strict.
- Mobile: Uso de Flutter Secure Storage (Keychain en iOS y AES en Android).
- Refresh Token Rotation: Mecanismo automático para invalidar sesiones ante cualquier intento de reutilización de tokens antiguos.
- Verify/Reset Safety: Tras
token/verifyoreset-password, rotar refresh y invalidar accesos previos.
4.2 Protección de Endpoints
- Rate Limiting: Máximo 10 intentos de login por cada 5 minutos por IP. Implementado en el nivel de Go (usando
golang.org/x/time/rate) para mitigar ataques DoS. - CORS: Política restrictiva permitiendo únicamente dominios oficiales de la organización.
- OWASP Headers: Implementación de Helmet.js para prevenir XSS, Clickjacking y Sniffing.
- Validación de Esquema: Uso de Protocol Buffers o JSON Schema para asegurar que los datos de entrada cumplan estrictamente con el Modelo de Dominio.
- Captcha Adaptativo: Activar tras patrones de abuso (5 intentos fallidos en login o verify) y en flujos de registro según reglas de producto.
- Detonantes de Captcha (referencia rápida):
- Login: 5 intentos fallidos por IP/correo.
- Refresh reuse detectado → exigir captcha en siguiente login.
- Registro: 5 fallos en
/auth/registero múltiples correos desde misma IP. - Verify OTP: 5 códigos fallidos o más de 3 reenvíos en 15 min.
- Forgot/Reset: 3 solicitudes fallidas en 15 min.
4.4 Protocolo de Comunicación
- Interna (Microservicios): gRPC para baja latencia y contratos estrictos (Protobuf).
- Externa (Client-to-Server): REST API sobre TLS 1.3.
5. Requerimientos No Funcionales (NFRs)
5.1 Rendimiento y Disponibilidad
- Disponibilidad: Objetivo del 99.9% (High Availability).
- Latencia: Consultas al buscador deben responder en menos de 300ms (P99) utilizando índices GIST de PostGIS y caché en Redis.
- Escalabilidad: Contenedores Docker sobre infraestructura elástica para soportar picos de tráfico.
5.2 Estrategia de Caché
- Nivel de Catálogo: Caché de larga duración (24h) para países y especialidades.
- Nivel de Red (Validación Triple): Caché de corta duración (30 min) con invalidación inmediata vía Pub/Sub al modificar convenios de aseguradoras.
5.3 Cumplimiento Legal (Data Privacy)
- PII (Personally Identifiable Information): Los teléfonos y correos deben encriptarse en la base de datos si no se usan para búsqueda.
- Borrado Lógico: No se eliminan registros físicos; se usa
deleted_atpara trazabilidad y auditoría médica. - Backups: Respaldos automáticos diarios con encriptación AES-256 y retención de 30 días.
5.4 Observabilidad
- Logs: Centralizados en formato JSON para análisis rápido. Estructurados en JSON para ingestión en [ELK Stack / Datadog].
- Tracing: Implementación de OpenTelemetry para rastrear peticiones desde Next.js hasta la base de datos a través de Go.
- Eventos Auth/Registro: Emitir
USER_LOGGED_IN,REFRESH_REUSE,OTP_VERIFIED,REGISTER_STARTED,REGISTER_COMPLETEDcontrace_id,user_id,ip,channelydevice_fingerprintcuando aplique.
5.5 Retención y Anonimización
- Retención PII: Anonimizar usuarios eliminados después de 24 meses manteniendo agregados estadísticos.
- PHI mínima en tránsito: No incluir diagnósticos ni notas clínicas en notificaciones; solo referencias de cita/doctor.
- Mascaramiento en UI/Logs: Ocultar parcialmente email/phone; prohibido loggear tokens y OTP.
- Roles acumulativos: PATIENT es base; al otorgar DOCTOR/ENTITY_ADMIN no se elimina el rol paciente. Validar scopes en backend.
- Auditoría de perfil: eventos
USER_PROFILE_UPDATEDyROLE_GRANTEDdeben registrarchanged_by, timestamp e IP/UA.
5.6 Resiliencia y DR
- RPO: ≤ 5 minutos (RDS PITR + backups horarios de Redis snapshots).
- RTO: ≤ 60 minutos para restaurar stack productivo (infra IaC + restore de DB + warm-up).
- Zonas/AZ: Despliegue multi-AZ para RDS y ECS; ALB cross-AZ.
- Chaos/Failover: Drill semestral de restauración y conmutación manual documentada.
5.7 Costos y Cuotas
- Rate Limits públicos: 100 req/min por IP en endpoints de búsqueda; 20 req/min en auth.
- Guardrails de almacenamiento: Tamaño máximo de adjuntos 5 MB; retención de logs en caliente 30 días, frío 180 días.
- Alerts Financieras: Alarmas de gasto diario >110% del promedio semanal en SES/SMS y egress CDN.
5.8 Checklist de Observabilidad por Servicio
| Servicio | Métrica P95 | Fallos/Errores | Eventos clave | Log/Trace keys |
|---|---|---|---|---|
| auth-service | login_latency_p95 < 300ms | tasa 401/422 < 2% | USER_LOGGED_IN, PASSWORD_RESET, REFRESH_REUSE | trace_id, user_id, ip, route, status |
| auth-service (registro) | verify_p95 < 350ms | fallos OTP < 5% | OTP_VERIFIED, REGISTER_STARTED, REGISTER_COMPLETED | trace_id, user_id, email, channel |
| catalog-service | catalog_geo_p95 < 300ms | 5xx < 1% | CATALOG_UPDATED | trace_id, user_id, resource, operation |
| search-service | search_p95 < 300ms | resultados vacíos < 10% | SEARCH_PERFORMED | trace_id, q, lat, lng, specialty_id, insurance_id |
| notification-service | notif_send_p95 < 2s | fallos < 2% | NOTIF_SENT, NOTIF_FAILED | trace_id, message_id, channel, template |
| appointments-service | appointment_create_p95 < 400ms | conflictos 409 < 5% | APPOINTMENT_CONFIRMED, APPOINTMENT_CANCELLED, NOSHOW | trace_id, appointment_id, doctor_id, entity_id, patient_id |
| availability-service | availability_p95 < 300ms | 5xx < 1% | SLOT_RESERVED | trace_id, doctor_id, entity_id, from, to |
| entity-service | entity_update_p95 < 400ms | 4xx < 3% | ENTITY_CREATED, AGREEMENT_ENABLED | trace_id, entity_id, agreement_id |
| dashboard-doctor | dashboard_p95 < 400ms | widget errors < 1% | DOCTOR_DASH_LOADED | trace_id, doctor_id, widgets |
| dashboard-entidad | dashboard_p95 < 400ms | widget errors < 1% | ENTITY_DASH_LOADED | trace_id, entity_id, widgets |
| profile-service | patch_profile_p95 < 400ms | 4xx < 3% | DOCTOR_UPDATED, PATIENT_UPDATED | trace_id, user_id, doctor_id |
Colas y DLQ: cada worker (notifs) debe exponer queue_depth, dlq_depth, processing_latency y alarmar si dlq_depth > 0 por >5 min.
5.9 Dashboards y consultas operativas (accionables)
Dashboards mínimos por servicio
| Dashboard | Panels obligatorios | Frecuencia de revisión |
|---|---|---|
auth-service-overview | requests por código, login p95, refresh reuse rate, errores OAuth | Diario |
search-service-overview | p95 búsqueda, vacíos por filtro, cache hit rate, errores DB | Diario |
appointments-service-overview | create p95, conflictos 409, cancelaciones, no-show rate | Diario |
notification-service-overview | envíos por canal, latencia, fail rate, dlq_depth | Cada 4h |
catalog-service-overview | lecturas p95, mutaciones admin, cache invalidations | Semanal |
Consultas de referencia (CloudWatch Logs Insights)
Errores por ruta (últimos 15 min):
fields @timestamp, route, status, trace_id
| filter status >= 500
| stats count() as errors by route, status
| sort errors desc
Latencia p95 por servicio (si se registra duration_ms):
fields service, duration_ms
| filter ispresent(duration_ms)
| stats pct(duration_ms, 95) as p95_ms by service
| sort p95_ms desc
Intentos fallidos de autenticación por IP:
fields @timestamp, ip, route, status
| filter route = "/api/v1/auth/login" and status in [401, 429]
| stats count() as failed_attempts by ip
| sort failed_attempts desc
| limit 50
Eventos críticos de seguridad:
fields @timestamp, event, user_id, trace_id, ip
| filter event in ["REFRESH_REUSE", "ROLE_GRANTED", "PROFILE_VERIFIED"]
| sort @timestamp desc
| limit 100
Consultas de referencia (PromQL, si aplica)
histogram_quantile(0.95, sum(rate(http_request_duration_ms_bucket{service="auth-service"}[5m])) by (le))
sum(rate(http_requests_total{service="appointments-service",status=~"5.."}[5m])) / sum(rate(http_requests_total{service="appointments-service"}[5m]))
max(queue_depth{service="notification-service"}) by (queue)
5.10 Routing de alertas por severidad
| Severidad | Criterio típico | Canal de alerta | Tiempo objetivo de respuesta | Escalamiento |
|---|---|---|---|---|
SEV1 | Caída total, corrupción de datos, auth indisponible general | Pager + Slack #incidents-sev1 + llamada | 5 min | Incident Commander + Tech Lead + DevOps |
SEV2 | Degradación severa p95 o error rate sostenido | Pager + Slack #incidents-sev2 | 15 min | Owner de servicio + QA Lead |
SEV3 | Falla parcial con workaround disponible | Slack #alerts-platform | 60 min | Owner de servicio |
SEV4 | Ruido operativo o riesgo bajo | Ticket automático backlog | 24 h | Owner de módulo |
Reglas:
- Toda alerta debe incluir:
service,severity,trace_id(si existe), métrica disparadora y enlace de dashboard. - SEV1 y SEV2 requieren postmortem (
template-postmortem.md). - Alertas repetidas sin acción en 2 semanas se revisan en hygiene review para tuning de umbrales.
5.11 Inventario inicial de dashboards y alarmas (nomenclatura oficial)
Prefijo estándar por entorno: salud-{env}- donde {env} es dev, stg o prod.
Dashboards CloudWatch
| Dashboard | Nombre oficial |
|---|---|
| Auth overview | salud-{env}-cw-dashboard-auth-overview |
| Search overview | salud-{env}-cw-dashboard-search-overview |
| Appointments overview | salud-{env}-cw-dashboard-appointments-overview |
| Notifications overview | salud-{env}-cw-dashboard-notifications-overview |
| Catalogs overview | salud-{env}-cw-dashboard-catalogs-overview |
| Platform SLO | salud-{env}-cw-dashboard-platform-slo |
Alarmas CloudWatch (mínimo operativo)
| Alarma | Nombre oficial | Umbral inicial | Severidad |
|---|---|---|---|
| Auth 5xx rate | salud-{env}-alarm-auth-5xx-rate | > 2% por 5 min | SEV2 |
| Auth login p95 | salud-{env}-alarm-auth-login-p95 | > 350 ms por 5 min | SEV3 |
| Search p95 | salud-{env}-alarm-search-p95 | > 400 ms por 5 min | SEV2 |
| Appointments conflicts | salud-{env}-alarm-appointments-409-rate | > 8% por 10 min | SEV3 |
| Notification DLQ depth | salud-{env}-alarm-notifications-dlq-depth | > 0 por 5 min | SEV2 |
| Catalog mutation failures | salud-{env}-alarm-catalogs-mutation-5xx | > 1% por 10 min | SEV3 |
| RDS CPU high | salud-{env}-alarm-rds-cpu-high | > 80% por 10 min | SEV2 |
| ECS task restart storm | salud-{env}-alarm-ecs-task-restarts | > 3 restarts por 10 min | SEV2 |
Canales de notificación (SNS/ChatOps)
| Destino | Nombre oficial | Uso |
|---|---|---|
| SNS SEV1 | salud-{env}-sns-incidents-sev1 | Pager + llamada + bridge |
| SNS SEV2 | salud-{env}-sns-incidents-sev2 | Pager + Slack |
| SNS Platform | salud-{env}-sns-alerts-platform | Alertas no críticas |
| Slack canal SEV1 | #incidents-sev1 | Coordinación incidente crítico |
| Slack canal SEV2 | #incidents-sev2 | Coordinación degradación severa |
| Slack canal Platform | #alerts-platform | Ruido operativo y seguimiento |
Reglas de mantenimiento:
- No crear dashboards o alarmas fuera de esta nomenclatura.
- Cualquier cambio de umbral debe quedar en changelog de release (
14-release-changelog.md). - Revisar umbrales al menos cada 30 días con datos reales.
5.12 Tabla de provisioning (Terraform)
Esta tabla define los campos mínimos para declarar dashboards/alarmas vía IaC.
| Tipo | name | metric_namespace | metric_name | comparison_operator | threshold | period_seconds | evaluation_periods | severity | target |
|---|---|---|---|---|---|---|---|---|---|
| alarm | salud-{env}-alarm-auth-5xx-rate | Salud/API | auth_5xx_rate | gt | 2 | 300 | 1 | SEV2 | salud-{env}-sns-incidents-sev2 |
| alarm | salud-{env}-alarm-auth-login-p95 | Salud/API | auth_login_p95_ms | gt | 350 | 300 | 1 | SEV3 | salud-{env}-sns-alerts-platform |
| alarm | salud-{env}-alarm-search-p95 | Salud/API | search_p95_ms | gt | 400 | 300 | 1 | SEV2 | salud-{env}-sns-incidents-sev2 |
| alarm | salud-{env}-alarm-appointments-409-rate | Salud/API | appointments_409_rate | gt | 8 | 600 | 1 | SEV3 | salud-{env}-sns-alerts-platform |
| alarm | salud-{env}-alarm-notifications-dlq-depth | Salud/Workers | notification_dlq_depth | gt | 0 | 300 | 1 | SEV2 | salud-{env}-sns-incidents-sev2 |
| alarm | salud-{env}-alarm-catalogs-mutation-5xx | Salud/API | catalog_mutation_5xx_rate | gt | 1 | 600 | 1 | SEV3 | salud-{env}-sns-alerts-platform |
| alarm | salud-{env}-alarm-rds-cpu-high | AWS/RDS | CPUUtilization | gt | 80 | 600 | 1 | SEV2 | salud-{env}-sns-incidents-sev2 |
| alarm | salud-{env}-alarm-ecs-task-restarts | ECS/ContainerInsights | task_restarts | gt | 3 | 600 | 1 | SEV2 | salud-{env}-sns-incidents-sev2 |
| dashboard | salud-{env}-cw-dashboard-auth-overview | N/A | N/A | N/A | 0 | 0 | 0 | N/A | N/A |
| dashboard | salud-{env}-cw-dashboard-platform-slo | N/A | N/A | N/A | 0 | 0 | 0 | N/A | N/A |
Ejemplo de estructura (terraform.tfvars.json) para alarmas:
{
"cw_alarms": [
{
"name": "salud-prod-alarm-auth-5xx-rate",
"namespace": "Salud/API",
"metric_name": "auth_5xx_rate",
"statistic": "Average",
"comparison_operator": "GreaterThanThreshold",
"threshold": 2,
"period": 300,
"evaluation_periods": 1,
"datapoints_to_alarm": 1,
"treat_missing_data": "notBreaching",
"severity": "SEV2",
"sns_topic_arn": "arn:aws:sns:us-east-1:123456789012:salud-prod-sns-incidents-sev2",
"runbook_url": "https://docs.example.com/runbooks/auth"
}
]
}
6. Matriz de Trazabilidad Técnica
| Entidad de Dominio | Endpoint API | Tabla Base de Datos | Paquete Go (Backend) |
|---|---|---|---|
| Doctor | /api/v1/doctors | doctor_profiles | internal/doctor |
| Clínica/Sede | /api/v1/entities | health_entities | internal/entity |
| Seguro | /api/v1/insurances | insurances | internal/insurance |
| Reseña | /api/v1/reviews | reviews | internal/doctor/reviews |
7. Convenciones Transversales de API
7.1 Formato de errores (estándar)
- Respuesta base:
{ "message": "texto", "trace_id": "..." }. - Códigos usados: 400 datos inválidos; 401 no autorizado; 403 prohibido; 404 no encontrado; 409 conflicto/duplicado; 422 datos semánticamente inválidos; 429 rate limit.
7.2 Versionado y deprecación
- Versión actual:
/api/v1. Para cambios breaking, publicar/api/v2y mantener v1 deprecado con fecha de retiro. - Anunciar deprecaciones en release notes y header
Deprecationcuando aplique.
7.3 Autenticación/Autorización
- JWT bearer para la mayoría de endpoints; refresh en cookie HttpOnly.
- Roles: PATIENT, DOCTOR, ENTITY_ADMIN, CATALOG_ADMIN y SUPERADMIN; scopes reflejados en rutas y trazabilidad.
- Endpoints públicos:
/api/v1/search/entities,/api/v1/search/doctors,/api/v1/verify.
7.4 Paginación y filtros
- Parámetros estándar:
page(default 1, min 1),page_size(default 20, máx 100). - Sort opcional en buscador:
sort=distance|rating|relevance.
7.5 Caché y ETags
- Catálogos (geo/taxonomía): caché 24h; invalidar tras cambios admin.
- Búsqueda/disponibilidad: caché corto (minutos) en Redis; evitar cache cliente cuando hay JWT.
- Usar
Cache-Control/ETagen recursos de solo lectura donde aplique.
7.6 Internacionalización y zonas horarias
- API entrega fechas en UTC ISO-8601; front convierte a zona del usuario.
- Errores/mensajes en español; otros idiomas vía i18n en front.
7.7 Seguridad y privacidad
- No loggear PII/PHI (emails, teléfonos, tokens, OTP). Mascarar cuando sea imprescindible.
- Retención: logs en caliente 30d, frío 180d; anonimizaciones según política.
7.8 Integraciones externas
- Email (SES/SendGrid), SMS (Twilio/Vonage), Push (FCM/APNS). Timeouts 5s, 3 reintentos exponenciales, DLQ si falla.
7.9 Feature flags y toggles
- Flags en
VITE_*(front) y env vars backend; documentar impacto (no-show, aprobación manual, cache bust).
7.10 Datos de prueba / fixtures
- Usar ejemplos en OpenAPI (login, availability, appointments, preferences) y seeds mínimas para QA manual.
7.11 Procesos de entrega
- Diseño-first:
static/openapi.yamles la fuente; actualizar y commitear en cada cambio de contrato. - Checklist PR: OpenAPI actualizado, docs alineadas, linters/tests, build Docusaurus.
7.12 Observabilidad y runbooks
- Cada servicio debe enlazar a su dashboard/alertas. Ver eventos en 5.8.
- Runbook mínimo: qué hacer ante 5xx sostenidos, DLQ>0, caída de proveedor de notifs.
7.13 Exposición de IDs y datos sensibles
- Exponer solo UUID/ULID públicos; evitar IDs autoincrementales o internos.
- Si hay PK interna numérica, usar
public_id(UUID) en DTOs y mapear internamente. - Validar ownership/rol en servidor aunque llegue un UUID (no confiar en el ID del cliente).
- No incluir campos innecesarios en responses (ej. emails completos, IP, refresh tokens); usar DTO mínimos por pantalla.
- Links a archivos/evidencias deben ser URLs firmadas y con expiración.
- Mascarar PII en logs y payloads cuando no sean estrictamente requeridos.