Sin API-to-API
La ingesta y la consolidación son límites independientes. La lectura no debe depender de la disponibilidad de la API de escritura.
Estudio de caso arquitectónico · laboratorio público
dotnet-observability-lab es una reference architecture educativa que utilizo para hacer verificable una línea completa de razonamiento: problema → restricciones → atributos de calidad → decisiones → implementación → fallas → observabilidad → evidencias.
Esta página es una lectura de portafolio. La documentación técnica canónica permanece en el repositorio del laboratorio.
Problema
Una API recibe un valor y necesita aceptarlo de forma durable e idempotente. Otro límite de servicio debe consolidar ese valor de forma asíncrona, sin llamadas HTTP entre API y sin convertir una redelivery del broker en una duplicación de negocio.
La verdadera dificultad no está en el valor decimal. Está en el intervalo entre persistir, publicar, consumir, confirmar y recuperar, incluso cuando no es posible saber con certeza si la etapa anterior terminó.
Restricciones y atributos de calidad
La ingesta y la consolidación son límites independientes. La lectura no debe depender de la disponibilidad de la API de escritura.
RabbitMQ puede realizar redelivery. Los duplicados forman parte del contrato y deben tratarse como un comportamiento normal.
PostgreSQL protege la idempotencia, Outbox, Inbox y el agregado. Redis es solo un atajo best-effort.
OpenTelemetry define traces, métricas y logs. Aspire compone y muestra el entorno local, pero no enruta el tráfico de negocio.
Decisiones y trade-offs
RabbitMQ desacopla la disponibilidad y la latencia entre la ingesta y la consolidación.
Trade-off: consistencia eventual y mayor costo operativo.
ADR 0004 ↗El valor y el evento pendiente se persisten en la misma transacción; un worker lo publica después con confirmación del broker.
Trade-off: polling, retry, cuarentena y posibilidad de publicación repetida.
Implementación ↗La identidad del mensaje y la actualización del agregado se confirman en la misma transacción PostgreSQL.
Trade-off: cada mensaje único exige trabajo durable y una futura política de retención.
Implementación ↗El diseño no promete exactly-once. Hace que la repetición sea segura para el efecto de negocio.
Trade-off: cada consumidor debe tratar explícitamente la identidad y la duplicación.
Contrato del evento ↗El caché reduce el costo de la repetición HTTP, pero una caída, expiración o eviction no pueden alterar la corrección.
Trade-off: los misses y las fallas todavía pueden llegar a la base de datos.
ADR 0003 ↗El contexto W3C cruza el límite asíncrono; Aspire es una de las superficies locales de inspección.
Trade-off: la propagación y la instrumentación deben mantenerse explícitamente.
ADR 0005 ↗Flujo nominal
Fallas relevantes
La misma key + el mismo valor devuelve el recibo original; un uso conflictivo devuelve 409. La restricción única en PostgreSQL resuelve la carrera concurrente.
Inbox evita un segundo efecto de negocio. El mensaje puede consumirse más de una vez sin volver a incrementar el agregado.
La API todavía puede aceptar y persistir la escritura. Outbox mantiene el trabajo pendiente para publicarlo después.
El fast path desaparece, pero no la corrección. La decisión de idempotencia vuelve a PostgreSQL.
El evento puede volver a publicarse con la misma identidad; el consumidor trata el duplicado de forma segura.
Inbox y el agregado hacen rollback juntos. No queda un marcador falso de procesamiento concluido.
Observabilidad
El contexto W3C iniciado en la solicitud se persiste junto con Outbox, el worker de publicación lo reinicia y se propaga mediante headers AMQP hasta el consumidor. Las métricas de baja cardinalidad diferencian aceptación, publicación, procesamiento y duplicación; los logs estructurados registran los resultados operativos relevantes.
El objetivo no es “tener dashboards”. Es poder explicar por qué una escritura todavía no apareció en el modelo de lectura y localizar el límite donde el flujo está detenido o repitiéndose.
Ver pruebas de traces y métricas ↗Evidencias públicas
Límites deliberados
El laboratorio mantiene un único PostgreSQL local con bases de datos lógicas separadas, no define una plataforma de deployment de producción y no implementa en v1 una política completa de bounded retry/DLQ en el consumidor. Tampoco promete ordenamiento de eventos ni exactly-once.
Estos límites son parte de la evidencia: muestran qué se decidió, qué se simplificó con fines educativos y qué decisiones deberían reabrirse en un sistema real.
Explorar el repositorio completo ↗