Saltar a contenido

Los módulos

El backend está dividido en módulos Spring Modulith. Cada uno tiene una API pública (lo que está en la raíz de su paquete) e internals privados. Acá: qué hace cada uno, su API pública y a quién llama. El porqué de esta estructura está en El monolito modular.

Mapa rápido

graph TD
    TICKETS["tickets<br/>orquestador"] --> CAT["catalogo"]
    TICKETS --> PAG["pagos"]
    TICKETS --> PRO["promos"]
    TICKETS --> ENV["envases"]
    SHARED["shared<br/>(kernel OPEN)"]
    MON["monitoring<br/>(Cockpit)"]
    SEC["security<br/>(app-wide)"]
Módulo Rol Llama a
tickets Orquestador. La API que ve el POS. catalogo, pagos, promos, envases
catalogo Artículos por EAN/sucursal + búsqueda. — (hoja)
pagos intent/pay/check/cancel. Stateless. — (hoja, sale a gateways)
promos Calcula promociones del ticket. — (hoja)
envases Vales y depósitos retornables. — (hoja)
shared Kernel OPEN: lengua común.
security Seguridad app-wide.
monitoring Cockpit / observabilidad. — (lee APIs públicas)

tickets — el orquestador

El corazón del backend: la API que consume la terminal y la que coordina a los demás módulos para armar, calcular y cerrar un ticket.

  • Servicios: TicketService (ciclo de vida del ticket), VoucherService (generación e impresión del comprobante), EanDecodeService (decodifica el EAN escaneado), TerminalSessionService (vestigio STOMP, muerto).
  • Controllers: TicketRestController (open/close/validate), PosRestController, TerminalSessionRestController, CacheRestController, EnvasesRestController, TemplateController, TestToolController.
  • Datos: Trx (el ticket), PaymentAttempt, PosConfig, DeviceConfig en la base TsAutoCompra.

Dos issues conocidos en VoucherService

  • Thread-safety (plan 007): un campo de config mutable compartido entre requests puede hacer que un thread pise la config de otro mientras imprime. La solución es aislar por thread.
  • getFirst() sin guarda (plan 008): un .getFirst() sobre lista vacía tira NoSuchElementException opaca si falta config; conviene fallar explícito con mensaje.

catalogo — artículos

Resuelve artículos por EAN y sucursal, y ofrece búsqueda paginada. Read-only sobre la base TipreRetail, con cache Caffeine de ~100k artículos.

  • Servicios: ArticuloService, ArticuloCacheService.
  • Controller: ArticuloRestControllerGET /searchBy (por id / codigoInterno / ean / descripcion / sucursal, paginado, máx. 200 ítems por página).
  • Admin: ArticuloCacheAdminControllerPOST /catalogo/cache/refresh (rol admin si la seguridad está activa).
  • Datos: Articulo (dm_Artic), Envase (projection).

pagos — cobros

Maneja el ciclo de pago contra los gateways. Stateless: no persiste, sale por WebClient a MercadoPago / RouterQR.

  • Servicios: PagoService, RealPaymentProcessorService, RouterQRBuilder.
  • Controller: PagoRestControllerPOST /payment-intent (async, devuelve Mono<ResponseEntity>), pay, check-status.
  • Borde externo: la salida a MercadoPago es red real; ahí viven la reconciliación durable y el manejo del estado indeterminado. Ver Pagos.

promos — promociones

Calcula las promociones aplicables a un ticket. Hoja, con cache propia.

  • Servicios: PromoService, PromosCacheService.
  • Controller: PromoControllerGET /promociones, POST /promociones/update (rol admin), POST /cache/refresh (rol admin).
  • Datos: promociones + auditoría de cálculos en TsPromos.

envases — vales y retornables

Gestiona envases retornables y los vales asociados.

  • Servicios: ValeService, EnvaseService.
  • Controllers: ValeController (@RequestMapping("/vales")GET /vales?codVale=, alta, update), EnvaseController.
  • Datos: Vale (@Audited con Envers → Vales_AUD), Envase en TsEnvases.

shared — kernel OPEN

La lengua común que cualquier módulo puede usar sin violar fronteras: ResponseMessage (la envoltura unificada de respuestas), los enums de pago y NucleoImpositivoDto (info impositiva). No tiene lógica de negocio.

security — app-wide

Un único SecurityFilterChain + JwtAuthConverter. Detalle en Backend → Seguridad.

monitoring — el Cockpit

Observabilidad del backend y del parque de terminales. Lee las APIs públicas de los demás módulos (no sus internals) y expone /monitoring/*. Ver Cockpit.