Saltar al contenido

Plataforma de crédito y obligaciones

ProCrédito v2

Plataforma de crédito distribuida: microservicios Laravel detrás de un API gateway, Redis Streams y workers en Python para procesamiento masivo de obligaciones.

Jefe de I+D de Software · Arquitectura · Soga SAS · 2019 — Actualidad

LaravelPHPPythonJSON:APIRedis StreamsMySQLNext.jsDockerKubernetesNginxHelm
Forma
Gateway + servicios Laravel + workers Python
Backbone asíncrono
Redis Streams
Store operativo
MySQL por cliente y por servicio

Panorama

ProCrédito v2 es una plataforma de crédito y obligaciones reconstruida como servicios desplegables por separado — no una segunda versión de un monolito con carpetas nuevas.

Los microservicios Laravel exponen JSON:API sobre slices hexagonales / DDD (seguridad, clientes, obligaciones, settings, terceros). Un API gateway autentica y enruta. Workspaces Next.js consumen esos contratos.

El trabajo masivo de obligaciones no vive en HTTP. Workers en Python consumen Redis Streams — ingestar, validar, escribir — mientras Laravel sigue siendo dueño del agregado de obligación y de la escritura de dominio.

Problema

Las operaciones de crédito ingieren archivos grandes de obligaciones, los validan contra reglas de negocio y los persisten sin bloquear el producto interactivo. Un solo request Laravel que parsea, valida y escribe ese volumen acopla la API de usuario a un job batch.

El dominio también está partido: identidad, clientes, obligaciones y bureaus de terceros no fallan ni se liberan juntos. Compartir modelos Eloquent entre esos concerns produjo un monolito distribuido.

La primera capacidad de intercambio no es “un importador”. Es una plataforma para intercambiar obligaciones: contratos, idempotencia y workers que se pueden reemplazar sin reescribir el agregado.

Restricciones

Los workers internos en Python no tienen API HTTP. Leen streams y escriben a través de contratos definidos. La comunicación entre servicios es eventos (Redis) o el gateway — nunca una sesión compartida de base de datos.

Las denegaciones esperadas son PolicyDecision + ErrorCode, no excepciones usadas como ramas de negocio. Nombres de campos, de streams y códigos de error viven en contratos — no como magic strings en los casos de uso.

MySQL es la fuente de verdad operativa, con una base por cliente y por microservicio (ms_*), de modo que el aislamiento de tenant es una regla de propiedad de datos, no un WHERE.

Arquitectura

El borde es grueso: el gateway termina auth y enruta a los servicios Laravel. Las reglas de negocio quedan detrás de ese borde. Las features nuevas de Laravel aterrizan en Application/Slices/{FeatureName} — Command, UseCase, Result, adaptador HTTP delgado.

La lógica de obligaciones en Python vive en domain/ y processors/, cableada desde consumers/runtime.py. El productor emite hechos a Redis Streams; los workers reaccionan. El productor no espera a cada consumidor.

La infraestructura (Eloquent, Redis, HTTP) se queda en adaptadores. El dominio no importa Laravel. Así un worker y un controller pueden compartir un caso de uso sin compartir un framework.

Architecture

ProCrédito v2 — gateway, servicios, streams

Apps Next.js

Archivos de obligaciones

API Gateway

Auth, ruta, límites

Security

Customers

Obligations

Third parties

Redis Streams

Hechos, no RPCs

Ingestor

Validator

Writer

MySQL

Por cliente · por servicio

Como está diseñada la plataforma v2: gateway en el borde, Laravel dueño del agregado, workers Python consumen Redis Streams. MySQL es el store operativo, aislado por cliente y por servicio.

Decisiones clave

Experiencia personal

Laravel es dueño del agregado; Python es dueño del pipeline

Las reglas de obligación y el write path se quedan en Laravel. Los workers Python transforman archivos y streams. Ese corte saca el procesamiento de alto throughput del request path sin convertir a los workers en un segundo dominio.

Experiencia personal

MySQL como fuente de verdad operativa, aislada por cliente

Cada microservicio es dueño de su schema. El aislamiento de tenant es una base por cliente, no un catálogo compartido con un client_id como ocurrencia tardía. Se consideró Oracle y se descartó para este plano operativo.

Experiencia personal

El gateway enruta; no calcula crédito

Autenticación, request IDs y routing pertenecen al borde. La política de obligaciones no. Un gateway que reimplementa reglas de dominio se vuelve un segundo dominio sin tests.

Experiencia personal

Contratos en lugar de Eloquent compartido

Los servicios se integran con payloads versionados y nombres de stream. Compartir modelos entre repositorios parece conveniente y produce un tren de releases del que ningún equipo puede salir.

Trade-offs

Se ganó

Las APIs interactivas siguen respondiendo mientras los archivos de obligaciones se mueven en una flota de workers que escala aparte. Los servicios pueden liberar sin un lock compartido sobre un solo schema.

Se sacrificó

Complejidad operativa: semántica de streams, idempotencia y la disciplina de no poner HTTP en workers internos. Un solo stack trace ya no explica una obligación de punta a punta.

Experiencia personal

Tecnologías

LaravelPHPPythonJSON:APIRedis StreamsMySQLNext.jsDockerKubernetesNginxHelm

Resultados

La plataforma v2 es la arquitectura del producto en marcha: gateway, servicios Laravel hexagonales, Redis Streams y workers Python — no un slide al lado de un monolito.

La primera capacidad de intercambio reemplazó la mentalidad de importador de archivos por un pipeline de obligaciones (ingestar → validar → escribir) que puede evolucionar sin reescribir el agregado.

A finales de 2026 el producto estaba en UAT. No se publican aquí cifras de throughput.

Lecciones

Un worker que “solo escribe la base” se vuelve el dominio si nadie es dueño del agregado. El corte solo se sostiene si Laravel sigue siendo la fuente de verdad del estado de la obligación.

El diseño event-driven es un buen trade cuando el productor no debería conocer a sus consumidores. Es un mal trade cuando la acción de cara al usuario es una mentira hasta que el efecto termina.

Los architecture packs le ganan al folklore de README. Cuando la regla es “slices, ports, Redis Streams, sin HTTP en workers”, el trabajo nuevo tiene dónde aterrizar.