Arquitectura e integración

Yrix Park está partido en procesos con un dueño claro: el borde lee, la API guarda y autoriza, el panel y la app operan. Esta página es el mapa para quien integra. No trae hosts, contraseñas, cadenas de conexión ni datos de una playa.

Los patrones de código (CQRS, repositorios, migraciones) y el flujo de permisos están en content/docs/arquitectura y content/docs/api del repositorio. Acá alcanza con saber qué pieza habla con cuál.

Borde, API y clientes

Piezas

PiezaTecnologíaResponsabilidad
ALPRParkingEnginePython, en el equipo de bordeLee la patente en el predio. La referencia de hardware es una Jetson. Yrix no la vende.
Parking.API.NET 8HTTP, MQTT, tiempo real y composición del proceso.
Parking.Domain.NETEntidades.
Parking.Application.NET, MediatRComandos y consultas.
Parking.InfrastructureEF Core, SignalR, handlers MQTTPersistencia, DetectionMqttHandler y HeartbeatMqttHandler.
Parking.IdentityOpenIddictAutenticación y roles.
Parking.Migrator.NETMigraciones por tenant. No corren solas al arrancar.
Parking.Worker.NETFacturación automática en segundo plano. La config es BillingRunner:*.
Parking.FrontEndAngularPanel de la playa.
Parking.MobileFlutter, AndroidOperación de Cabina y Atención.
Parking.WebNext.jsEste sitio y esta documentación. No cobra ni abre la barrera.

Recorrido de una detección

Cámara → borde (ALPR) → MQTT → Parking.API → PostgreSQL
Parking.API → SignalR → panel Angular y app
  1. La cámara entrega video al equipo de borde. El motor publica la detección y el heartbeat por MQTT. Los topics están en docs/architecture/jetson-resilience.md del repo Parking.API.
  2. DetectionMqttHandler persiste la detección. HeartbeatMqttHandler registra el heartbeat.
  3. La API avisa al grupo SignalR del tenant. El evento de una lectura nueva es NewDetection.
  4. El panel y la app actualizan detecciones. La ocupación se entera por OccupancyChanged, no por el heartbeat de la cámara.

El mismo canal lleva DeviceStatusChanged, NewNotification y NewParkingIncident. El hub es /hubs/parking, con el JWT del operador. El transporte documentado es WebSocket. En el despliegue, Nginx queda delante del API.

Si el predio se queda sin red, el controlador retiene las lecturas y las publica por MQTT al reconectar. La API no se entera de un cuadro que todavía no salió del predio.

Multi-tenant

Cada estacionamiento es un tenant:

  • Tiene su propia base PostgreSQL.
  • TenantMiddleware lee tenant_id del JWT.
  • SignalR aísla los eventos en el grupo de ese tenant.
  • Las migraciones de negocio las aplica Parking.Migrator, tenant por tenant.

Un integrador no elige la base en el request. La elige el token.

Cómo se integra

Tres bordes, y ninguno pide una IP publicada en esta guía:

BordeUso
REST api/v1/...Consultas y comandos. La respuesta de negocio envuelve el resultado. Un error de regla (punto de venta repetido, por ejemplo) vuelve como error de negocio, no como un 200 vacío.
OpenIddictUsuario y contraseña. El JWT trae los permisos de los roles. [RequirePermission("Recurso.Acción")] rechaza con 403 si falta el claim.
SignalR /hubs/parkingPush al operador que ya tiene sesión. No reemplaza al REST: avisa que hay que volver a leer.

La app móvil y el panel usan el mismo usuario de operador y el mismo hub. La app, en la versión documentada, no usa PKCE: entra con usuario y contraseña y conserva un refresh token.

El borde no se integra por HTTP público desde la cámara. La cámara habla RTSP con el equipo de la playa. Ese equipo habla con la API por MQTT. No hace falta abrir el video a internet para que el panel vea la patente.

Qué queda afuera de esta página

  • Direcciones de servidores, nombres de entorno y secretos.
  • Credenciales de MQTT, del proveedor fiscal y de Mercado Pago.
  • Patentes, fotos o importes de un cliente.
  • Un porcentaje de precisión del ALPR. La confianza es por detección, como explica Lectura de patentes.

El circuito de caja, comprobante y roles está en Gestión del estacionamiento.

Arquitectura e integración · Yrix