Inicio/Microservicios Java/Consistencia distribuida y checkout
Cargando video...

Consistencia distribuida y checkout

¿Duda de esta lección?Te respondemos en #java

Consistencia distribuida y checkout

J

José Díaz

Fundador de JoeDayz y JefToken | Arquitecto Senior

Microservicios Java

Sagas, CQRS, la plataforma e-commerce multi-tenant y cómo un checkout viaja desde el tenant_id hasta Kafka.

Sumario

Qué vas a ver

Cómo se mantiene la consistencia cuando el pedido, el pago y el stock viven en servicios distintos, y cómo ese diseño se ejecuta: multi-tenant, virtual threads y el flujo de checkout.

Transactional outbox

Escribir el pedido en la base y publicar el evento en Kafka por separado es una escritura dual. Si el pedido queda grabado y el evento no sale, el inventario se desincroniza.

En una sola transacción:

  1. Insert del pedido.
  2. Insert en la tabla outbox.

Un poller o Debezium (CDC) publica después a Kafka. La garantía es at-least-once, así que los consumidores tienen que ser idempotentes.

Saga orquestada

OrderSaga coordina el camino feliz: pago y después stock.

Si Stock responde StockInsufficient, no hay rollback distribuido. Hay compensación: reembolso del pago y el pedido pasa a CANCELLED.

La consistencia es eventual, mediante un undo semántico. La orquestación centralizada sirve para flujos complejos.

CQRS y BFF

Las operaciones se separan en dos modelos:

  • Write model: el dominio, normalizado.
  • Read model: desnormalizado y rápido. Se sincroniza de forma asíncrona.

Cada canal tiene su backend:

  • BFF de la app móvil: payloads ligeros.
  • BFF del panel admin: datos ricos y CRUD.

Eso reemplaza a la God API.

La plataforma

Clientes mobile y web entran con JWT por el API Gateway y Keycloak. Detrás están los BFF y los servicios:

ServicioPersistencia
OrderPostgreSQL
CatalogPostgreSQL + Elasticsearch
InventoryPostgreSQL
Payment—
NotificationMongoDB

Todos publican al bus de eventos (Kafka). La observabilidad es OpenTelemetry y la malla de servicios es Istio.

Multi-tenant

Tres formas de aislar, de más cara a más escalable:

EstrategiaQué implica
DB por tenantAislamiento máximo, costo alto
Schema por tenantAislamiento medio
Schema compartido + tenant_idEscala SaaS

Toda query, todo evento y toda caché llevan el tenant_id.

Del ThreadLocal al ScopedValue

ThreadLocal es frágil en un pool de hilos: es mutable y el contexto de un tenant se puede filtrar al request siguiente.

ScopedValue (Java 25+) es inmutable y queda encapsulado en un structured scope. Encaja con virtual threads. TenantContext inyecta ese token desde el gateway hasta JPA.

ScopedValue.where(TENANT_ID, "empresa-123").run(() -> {
    procesar();
});

Virtual threads

Un platform thread bloqueado en I/O limita la escala y sube la latencia. Un virtual thread es liviano: el código sigue siendo bloqueante, pero el flujo no ocupa un hilo del sistema.

Spring MVC o Quarkus REST sobre virtual threads mantienen el código imperativo y el modelo de dominio, sin la complejidad de WebFlux.

El checkout, de punta a punta

  1. Entrada. ScopedValue captura el tenant_id y un virtual thread toma el I/O.
  2. Validación. Order, el aggregate root, valida las reglas.
  3. Outbox. En una transacción se guarda el pedido en estado PLACED y el evento.
  4. Kafka distribuye el evento.
  5. Saga. OrderSaga orquesta pago, stock y envío. CQRS actualiza las vistas en background.

El mandato

  1. Diseñá por dominio: protegé las invariantes con DDD, aislá bounded contexts y definí fronteras duras.
  2. Escalá con concurrencia moderna: virtual threads, ScopedValue y concurrencia estructurada en Java 21+.
¿Duda de esta lección?Te respondemos en #java

Contenido

Completa el curso y obtén tu certificado

11 actividades restantes