Cómo construir un microservicio paso a paso. Una receta única replicable para los 25.
X-Request-Id · cifrado de campo con AES-GCM.// Patrón Hexagonal / Ports & Adapters por servicio com.toscana.sih.<servicio> ├── domain // Entidades + reglas de negocio (puro Java, sin frameworks) │ ├── model │ ├── service │ └── port // Interfaces hacia afuera (in/out) ├── application // Casos de uso · orquestación │ └── usecase ├── infrastructure // Adaptadores │ ├── persistence // Spring Data JPA / MongoTemplate │ ├── messaging // Productores y consumidores Kafka │ ├── http // Clientes REST hacia otros servicios │ └── external // RENAP / SICOIN / FHIR ├── interfaces // Adaptadores entrantes │ ├── rest // Controladores REST │ ├── ws // WebSockets │ └── event // Listeners Kafka └── config // Beans Spring · Security · Observabilidad
sih-<servicio>-svc desde la plantilla de GitHub toscana-sih/template-svc. Ya trae Maven, Dockerfile, helm chart, Makefile y workflow CI.domain/model: clases inmutables (Java records cuando posible), invariantes en el constructor, sin anotaciones de framework.domain/port: RepositorioPaciente, PublicadorEventos, ClienteRenap. Son interfaces puras.application/usecase: una clase por caso (RegistrarPaciente, FusionarPacientes). Reciben puertos por constructor.infrastructure/persistence: implementa RepositorioPaciente con Spring Data JPA. Tabla con created_at, updated_at, deleted_at, version.PublicadorEventosKafka serializa a Avro/JSON y publica a sih.<contexto>.<evento> con clave paciente_id.interfaces/rest: usa @RestController, valida con @Valid, mapea a DTO, maneja errores con @RestControllerAdvice.db/migration/V001__init.sql. Nunca modificar una migración aplicada — siempre crear una nueva./actuator/health.deploy/chart: values por entorno (DEV/QA/STAGING/PROD-HNSM) con secretos vía SealedSecrets.# Recurso plural en kebab-case · versión en path
GET /api/v1/pacientes/{id} → 200 PacienteDto | 404
POST /api/v1/pacientes → 201 + Location · Idempotency-Key obligatoria
PATCH /api/v1/pacientes/{id} → 200 PacienteDto · usa JSON Merge Patch
DELETE /api/v1/pacientes/{id} → 204 (soft delete)
GET /api/v1/pacientes?cui=...&limit=20 → 200 Page<PacienteDto>
# Errores: RFC 7807 (Problem Details)
{
"type":"https://sih.hnsm/errors/validation",
"title":"CUI inválido",
"status":400,
"detail":"El CUI 1234567890123 no pasa el algoritmo verificador",
"instance":"/api/v1/pacientes",
"trace_id":"abc-123"
}
# Topic: sih.<contexto>.<evento> ej. sih.identidad.paciente.creado # Clave: paciente_id → asegura orden por paciente # Esquema (JSON): { "id":"01JEVENTID...", // ULID "type":"paciente.creado", "ts":"2026-05-15T08:30:00-06:00", "actor":"u-Admin-HNSM-001", "trace_id":"abc-123", "data":{ "paciente_id":"...", "cui":"..." }, "version":1 }
v1$base64(iv)$base64(ciphertext)$base64(tag).v2$...; el lector entiende ambas hasta el cierre de la migración.domain no depende de Spring/JPA.docs/runbook.md).