El Desaf铆o Arquitect贸nico
En plataformas de inversi贸n distribuidas, procesar inyecciones de capital y operaciones de intercambio de divisas presenta un desaf铆o cr铆tico de concurrencia y consistencia de datos. La falta de aislamiento transaccional estricto ante peticiones HTTP simult谩neas puede provocar Race Conditions, derivando en balances inconsistentes o p茅rdida de integridad financiera. El objetivo principal fue dise帽ar un motor transaccional robusto alineado con las propiedades ACID, capaz de mitigar mutaciones concurrentes a nivel de infraestructura y exponer un Sandbox interactivo de alta fidelidad sin comprometer la estabilidad del hardware.
Soluci贸n T茅cnica Multicapa
Para resolver este desaf铆o de manera integral, se estructur贸 un ecosistema desacoplado y blindado en ambas capas operativas:
-
Backend (Motor de Alta Disponibilidad y Concurrencia): Arquitectura empresarial basada en Spring Boot 3 y Java 21. El servicio implementa un esquema de Bloqueo Pesimista (
PESSIMISTIC_WRITE) mediante Spring Data JPA, forzando a PostgreSQL a congelar la fila del registro financiero durante las mutaciones, garantizando consistencia absoluta ante tr谩fico paralelo masivo. El per铆metro HTTP est谩 asegurado mediante validaciones declarativas estrictas (@Validde Jakarta), procesadas por un interceptor global de excepciones (@ControllerAdvice) que formatea respuestas limpias para el cliente. La capa de infraestructura optimiza el pool de conexiones mediante HikariCP y empaqueta el entorno en im谩genes optimizadas con Multi-stage Alpine Docker para reducir dr谩sticamente el tiempo de inicio. -
Frontend (Sandbox de Observabilidad y UX Defensiva): Una interfaz SPA reactiva integrada de forma nativa en Astro mediante componentes aislados de React. El dashboard implementa estados de env铆o restrictivos (
isSubmitting) y filtros l贸gicos por expresiones regulares en inputs de texto, bloqueando caracteres no param茅tricos desde la interfaz de usuario. El control as铆ncrono gestiona de forma preventiva las Race Conditions mediante la emisi贸n de Custom Events en el DOM que congelan la barra de navegaci贸n de Astro durante las transacciones de red. Adem谩s, el flujo de actualizaci贸n separa el renderizado inicial de los actualizaciones de datos en segundo plano, previniendo el colapso del 谩rbol de componentes.
Fragmento de Implementaci贸n
@Transactional
public Portafolio comprarUsdc(Long usuarioId, BigDecimal montoMxn, BigDecimal tipoCambio) {
if (montoMxn.compareTo(BigDecimal.ZERO) <= 0 || tipoCambio.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("El monto y el tipo de cambio deben ser mayores a cero.");
}
// Bloqueo a nivel de BD:
// Evita que el saldo mute entre la lectura y la deducci贸n del Swap
Portafolio portafolio = portafolioRepository.findByUsuarioIdForUpdate(usuarioId)
.orElseThrow(() -> new IllegalArgumentException("No se encontr贸 un portafolio activo para el usuario: " + usuarioId));
if (portafolio.getBalanceMxn().compareTo(montoMxn) < 0) {
throw new IllegalStateException("Saldo insuficiente para realizar la compra.");
}
// Ejecuci贸n de transacciones
BigDecimal nuevoBalanceMxn = portafolio.getBalanceMxn().subtract(montoMxn);
portafolio.setBalanceMxn(nuevoBalanceMxn);
BigDecimal usdcComprados = montoMxn.divide(tipoCambio, 4, RoundingMode.HALF_UP);
BigDecimal nuevoBalanceUsdc = portafolio.getBalanceUsdc().add(usdcComprados);
portafolio.setBalanceUsdc(nuevoBalanceUsdc);
return portafolioRepository.save(portafolio);
}