Skip to content

feat: implementación completa del pipeline de liquidación de pagos#6

Open
LuisEnrique23 wants to merge 1 commit into
yaperos:mainfrom
LuisEnrique23:challenge/luis-moreno
Open

feat: implementación completa del pipeline de liquidación de pagos#6
LuisEnrique23 wants to merge 1 commit into
yaperos:mainfrom
LuisEnrique23:challenge/luis-moreno

Conversation

@LuisEnrique23
Copy link
Copy Markdown

¿Por qué elegí este challenge?
Elegí el Challenge 1 porque expone los problemas fundamentales de sistemas distribuidos en fintech: consistencia eventual, entrega confiable de mensajes y tolerancia a fallos parciales.

Decisiones arquitectónicas

Se implementa el patrón Transactional Outbox junto con un relay independiente para garantizar entrega at-least-once, evitando inconsistencias entre la base de datos y el broker de mensajería. El relay se ejecuta como un proceso separado, lo que permite su escalamiento y monitoreo sin depender del ciclo de vida de la aplicación principal.

La idempotencia se resuelve en los consumidores mediante el uso de un identificador único de evento (eventId), apoyado por una tabla processed_events con una restricción de unicidad sobre (eventId, consumerName), lo que evita el reprocesamiento ante entregas duplicadas.

Se utiliza PostgreSQL como base de datos por su soporte robusto de transacciones ACID, la capacidad de manejar estructuras flexibles con JSONB y funcionalidades como SELECT ... FOR UPDATE SKIP LOCKED, fundamentales para el procesamiento concurrente del outbox.

La transición de estados del pago se gestiona mediante una saga basada en eventos. El componente de orquestación consume el evento payment.created.v1, espera la recepción de confirmaciones de los servicios de fraude y contabilidad, y luego actualiza el estado del pago de forma reactiva, evitando mecanismos de polling.

Se implementa un Dead Letter Topic (DLT) para manejar mensajes que no pueden procesarse tras múltiples intentos. Estos mensajes se envían a payment.created.v1.dlt junto con información de error, lo que permite su análisis y eventual reprocesamiento.

Para el control de concurrencia en el relay, se utiliza FOR UPDATE SKIP LOCKED, lo que permite ejecutar múltiples instancias en paralelo sin riesgo de procesar el mismo registro más de una vez.

¿Qué haría diferente con más tiempo?

  • Migraciones TypeORM en lugar de synchronize: true para gestión de esquema en producción.
  • Schema Registry (Confluent) para versionado de contratos de eventos y compatibilidad backward/forward.
  • Tests unitarios y de integración con testcontainers para PostgreSQL y Kafka.
  • Health checks (/health) para el relay y los consumidores.

Limitaciones y atajos conocidos

  • synchronize: true: Las tablas se auto-crean al iniciar. En producción se usarían migraciones versionadas.
  • Relay con polling cada 1 segundo: En producción se podría usar PostgreSQL LISTEN/NOTIFY o CDC (Debezium) para latencia sub-segundo.
  • FraudConsumer simplificado: El scoring de fraude usa una heurística básica con jitter aleatorio, no un modelo ML real.
  • Sin tests automatizados: La validación se realizó con pruebas end-to-end manuales.
  • Sin autenticación/autorización: Los endpoints están abiertos. En producción se implementaría JWT/OAuth2.
  • Configuración hardcoded en .env: Las credenciales deben manejarse con un vault (HashiCorp Vault).

…hallenge 1)

- Arquitectura event-driven con Kafka y patrón Transactional Outbox
- Consumidores idempotentes (Fraud, Ledger, Notify) con deduplicación por eventId
- Relay independiente que publica eventos pendientes desde PostgreSQL a Kafka
- Reintentos configurables (3 intentos) con Dead Letter Topic (DLT)
- Status Saga que orquesta la transición final (pending a settled/failed)
- Endpoint GET con metadatos de consistencia eventual
- Docker Compose con PostgreSQL 16, Kafka y Zookeeper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants