La Dirección General de Tráfico (DGT) de España ha establecido como obligatorio el uso de la baliza V-16 en carretera a partir de este 2026. Más allá de toda la polémica en cuanto a la utilidad de la medida, este post tiene por objetivo proponer un sistema que dé soporte a estos dispositivos IoT.
⚠️ Nota: Este post es largo y 100% escrito a mano (0% IA). Si quieres saltar directamente a la arquitectura propuesta haz click aquí. Aunque te recomiendo leerlo entero ;)
Capítulo 0: Metodología
Este post va a seguir una metodología de varios pasos:
- Entender el Problema y detectar limitaciones
- Identificar a los Principales Actores
- Definir Requisitos Funcionales
- Definir Requisitos No Funcionales
- Establecer una API de alto nivel (diagrama de flujo)
- Diseñar para los Requisitos Funcionales
- Ajustar para los Requisitos No Funcionales
Estos puntos no se corresponden uno a uno con los capítulos pero me parece importante dejar esta intro para que se entienda la metodología de diseño que voy a seguir.
Capítulo 1: Los primeros requisitos.
El equipo de la DGT 3.0 recibe un aviso: "A partir de 2026 tendrá que estar operativo el sistema para recibir eventos de la Baliza V-16. Todos los coches de España deberán disponer de una y hacer uso de ella en caso de emergencia en carretera."
La información, aunque no es demasiado extensa, ya deja entrever algunos retos técnicos. Aquí algunas de las primeras preguntas y datos clave:
- ¿Debemos conectar las balizas directamente a nuestro sistema?
Esto lo discutiremos más adelante y veremos qué se ha decidido finalmente. Spoiler: NO
- ¿A qué volumen de usuarios debemos dar soporte?
Haciendo una búsqueda rápida, según datos de 2023 en España había 23 millones de automóviles asegurados. Poniéndonos en la peor situación, imaginemos que todos circulan todos los días y que tenemos una tasa diaria de incidencias del 0,1%. Eso serían 23.000 vehículos enviando señales durante el día. Obviamente esto es una barbaridad, pero como primera aproximación nos podemos hacer a la idea de que el volumen de datos va a ser grande.
- ¿Cómo de importante es la información y cómo de crítico es el sistema?
La seguridad en carretera es algo primordial. Necesitamos un sistema robusto y con alta disponibilidad. Perder un evento puede marcar la diferencia entre tener un coche señalizado y no tenerlo.
- ¿De qué base tecnológica partimos?
Sinceramente, no he encontrado mucho. Este ejemplo parte de la premisa de que la DGT dispone de un cloud que nos permita desplegar microservicios. Como se trata del diseño del sistema, los detalles específicos sobre proveedores los dejaremos a un lado.
Capítulo 2: Empezando a Pensar el Sistema
¿Por qué no conectar las balizas a la DGT directamente? La respuesta es otra pregunta: Si la API fuese abierta, ¿cómo asegurarías que un actor malintencionado no intenta engañarte y llenar el mapa de señales?
Definitivamente esta opción es poco viable. Por eso la DGT ha habilitado la API de ingesta de datos solo para empresas verificadas previamente (y entiendo que serán además las que tengan balizas certificadas). De su propia documentación y especificación:
Por supuesto, esta decisión tiene un impacto en las características del sistema:
- Hemos simplificado la gestión de ataques DDoS, tenemos un sistema más sencillo y seguro.
- Pero hemos sacado de nuestro control una parte de la estabilidad del sistema. Si el cloud del proveedor de las balizas cae, no recibiremos eventos.
Siguiente tema: cómo identificar las incidencias.
Queremos saber qué incidencias tenemos en tiempo real y eso técnicamente implica saber qué balizas están activas. Una solución simple podría ser un sistema basado en 2 eventos: uno de encendido (start) y otro de apagado de la baliza (end)
Pero, ¿qué pasaría si alguno se pierde? Podría haber faltas de coherencia: eventos de final que llegan sin haber empezado, eventos de inicio que nunca terminan... En este caso una solución es añadir un tercer evento, de tipo "ping", que se repita en el tiempo y que nos permita solventar posibles errores en la red, pérdidas de mensajes o incluso fallos del hardware de la baliza.
Esta segunda solución nos permite establecer un Time-To-Live, un periodo de tiempo durante el cual asumimos que la baliza está activa, si pasado ese periodo la baliza no envía más mensajes, incluso sin haber enviado el evento de final, podemos asumir que la incidencia ha finalizado.
Un poco más de Matemáticas
La documentación de la DGT establece que el formato del evento debe ser el siguiente:
{
"actionid": "1",
"detection_time": "2020-02-24T07:17:27Z",
"lon": -3.5 ,
"lat": 40.5,
"device_event_type": 1,
"device_event_type_value": 1,
"information_quality": 1
}
Esto son 189 Bytes.
La baliza debe tener batería suficiente para media hora. Suponiendo un mensaje "ping" cada 30 segundos esto nos deja con 60 mensajes totales por cada incidencia.
Nota del David del futuro: la especificación oficial establece este ping cada 100 segundos, pero tampoco es relevante.
Tamaño Incidencia = 189 B * 60 = 11340 B
No es mucho. Continuando con nuestra suposición de 23.000 incidencias diarias, el volumen total diario asciende a 260 MB diarios. Anualmente: 94GB.
No es una cifra astronómica, con la tecnología actual el almacenamiento no será un problema. Además, esto es el tamaño del mensaje JSON, en base de datos habrá menos duplicidades (las keys no se guardan, solo los valores).
Capítulo 3: Requisitos Funcionales
Ahora que tenemos una idea de cómo hacer que esto funcione, toca establecer qué es lo que el sistema tiene que hacer.
Tenemos identificados a nuestros dos principales usuarios:
- Proveedores de Datos V-16
- Consumidores de la plataforma DGT 3.0
El sistema:
- debe permitir a los Proveedores de Datos V-16 iniciar sesión y recibir un token de acceso temporal.
- debe permitir a los Proveedores de Datos V-16 enviar los eventos de las diferentes balizas e informarles de la recepción del mismo.
- debe permitir a los Consumidores de la plataforma DGT 3.0 obtener los datos de incidencias en tiempo real y obtener actualizaciones sobre esos datos.
- debe permitir a los Consumidores de la plataforma DGT 3.0 la consulta de datos históricos de incidencias.
Requisitos Excluidos del Proyecto
Para este ejemplo he dejado fuera el registro de las Entidades proveedoras de Datos V-16, dado que necesitan de una certificación manual y no es relevante para el problema real: el manejo de grandes volúmenes de mensajes en tiempo real.
También queda fuera la gestión de usuarios de la plataforma DGT 3.0 (registro y login), dado que es un sistema que ya existe. Simplemente aparecerá en nuestros esquemas pero no profundizaré en él.
Capítulo 4: Requisitos No Funcionales
Como ya hemos visto en el capítulo 2, necesitamos un sistema robusto y rápido, que refleje lo más fielmente posible la realidad de las carreteras.
Por eso:
- Las confirmaciones de recepción de eventos deben realizarse en el menor tiempo posible. Objetivo: <500 ms.
- Las incidencias deben aparecer en la plataforma en menos de 5 segundos desde que se iniciaron.
- Disponibilidad del sistema: SLA 99,99% (menos de 1 hora de downtime anual)
Capítulo 5: Diseño del sistema para cumplir con los requisitos funcionales.
Antes de entrar al diagrama de arquitectura, no está de más pararse a plantear cómo serán las interacciones en el sistema. Para ello he preparado este diagrama de flujo:
Básicamente tenemos a nuestros dos tipos de clientes:
- V-16 Data Providers
- DGT 3.0 Data Consumers
- Y a nosotros mismos (la DGT 3.0)
Junto a las acciones e interacciones con nuestro sistema. Vamos a ir construyendo poco a poco el sistema.
5.1 - El Login de Proveedores
Este es sencillo. Para el Login usaremos un servicio preexistente de Users y Auth.
De la documentación oficial de la DGT:
Como ves existe una especificación muy concreta, pero no entraré en ella para centrarme en el diseño del sistema.
5.2 - La Ingesta de Eventos
Para este segundo flujo vamos a disponer de un total de tres microservicios.
El servicio "Event Validator & Ingestor" validará el evento recibido y, en caso de ser correcto, lo enviará a una cola de mensajes para ser consumido por los otros dos servicios. Esta validación será rápida ya que solo incluye el formato y pequeñas comprobaciones:
Dependiendo de si queremos devolver un error en caso de recibir alertas de un evento del que no hemos recibido su evento de inicio, podemos añadir una llamada interna a nuestro servicio de Device Presence.El servicio de "Device Presence" se enfoca en los eventos activos. Hace uso de una base de datos key/value en memoria (por ejemplo Redis) muy rápida, perfecta para almacenar las incidencias activas y permitir su consulta con bajas latencias.
El servicio de "Incidents History" se enfoca en la visión histórica. En este caso he elegido una base de datos SQL para almacenar la información a largo plazo. La idea es permitir la consulta para datos estadísticos y de mejora de carreteras (por ejemplo detectando puntos calientes).
Además, al añadir el "Event Validator & Ingestor", he considerado conveniente añadir un API Gateway para exponer de manera unificada tanto este como el servicio de Usuarios. Esto además nos permitirá cumplir con los criterios de seguridad relativos a las IPs desde las que se accede a la plataforma.
El uso de la cola de mensajería en este caso está justificado por varios motivos:
- Nos permite enviar el evento desde el "Event Ingestor" a dos microservicios diferentes.
- Nos permite desacoplar los servicios y asegurar que no se pierda ningún evento incluso aunque alguno de los servicios de almacenamiento pueda llegar a caer o estar saturado.
- También da cumplimiento a ciertos requisitos no funcionales (lo veremos más adelante).
De momento con esto habríamos cumplido con los requisitos del primer grupo de clientes, los proveedores de datos V-16.
5.3 - Información en tiempo Real para los usuarios de DGT 3.0
Para la conexión de nuestros usuarios utilizaremos el servicio de Usuarios y Auth, y crearemos un servicio en exclusiva para el tiempo real "Real Time Incidents Service". ¿Por qué uno nuevo y no el "Device Presence Service"? Para mantener a este stateless.
El servicio de Real Time va a mantener conexiones abiertas (Web-Sockets o Server Sent Events). Para evitar meter esta carga a nuestro servicio de almacenamiento, vamos a mantenerlos separados. El servicio de tiempo real pedirá al de almacenamiento las incidencias activas y escuchará de una cola los cambios publicados por el servicio de Device Presence Service.
Y con esto ya tendríamos la arquitectura completa cumpliendo con los requisitos funcionales:
Capítulo 6: Perfilando el Sistema para Cumplir con los Requisitos No Funcionales.
Los vuelvo a traer aquí para refrescar la memoria:
- Las confirmaciones de recepción de eventos deben realizarse en el menor tiempo posible. Objetivo: <500 ms.
- Las incidencias deben aparecer en la plataforma en menos de 5 segundos desde que se iniciaron.
- Disponibilidad del sistema: SLA 99,99% (menos de 1 hora de downtime anual)
Para cumplir con nuestro primer requisito ya tenemos casi todo. Al haber desacoplado el "Event Ingestor" del servicio de Almacenamiento, podemos responder rápidamente sin tener que esperar a guardar el evento.
Como extra, podemos redundar el servicio y añadir un Balanceador de Carga (Load Balancer) para asegurar disponibilidad y poder hacer frente a incrementos estacionales, por ejemplo las operaciones entrada y salida de vacaciones.
Los dos últimos también los tenemos bastante encaminados. Al utilizar una base de datos clave-valor en memoria, las operaciones de lectura y escritura son muy rápidas. El sistema de colas además nos permite leer los eventos desde diferentes instancias del servicio.
En general, podemos duplicar tanto los servicios como las bases de datos para asegurar la alta disponibilidad y el escalado en caso de ser necesario. Esto dejará a nuestras bases de datos con una configuración AP en referencia al teorema CAP (Availability + Partition Tolerance). Tendremos por tanto consistencia eventual.
El sistema queda diseñado finalmente de la siguiente manera:
Capítulo 7: Últimos comentarios y puntos de mejora.
Hay ciertos temas en los que no he profundizado, más que nada porque ya están definidos en la documentación de la DGT y por tanto no aportaban mucho valor. Un dato clave para entender más a fondo todo esto es el actionid:
Durante todo el ciclo de vida de la incidencia existe un identificador único para cada baliza e incidencia. Es decir, una vez se apaga la baliza, el siguiente mensaje tendrá un ID de incidencia nuevo.
Un último apunte. Externalizar la recepción desde las balizas no evita que los propios proveedores no puedan ser víctimas del ataque y por tanto nosotros también como receptores finales de los eventos. No me he centrado en ello pero se podría implementar un servicio de análisis de transacciones para detectar posible actividad inusual (por ejemplo mensajes con localizaciones que no corresponden con carreteras) y de esta forma asegurar la calidad de los datos.
Y con esto voy terminando. No sé cómo estará implementado realmente el cloud de la DGT, sería interesante conocerlo. Sin embargo, desde fuera hay algunos detalles que me han llamado la atención en el diseño (ya más a bajo nivel) y para los que voy a dejar propuestas de mejora:
Inconsistencias en el Naming
El primero de ellos es la consistencia (o falta de ella) en el nombrado de las variables:
Tenemos idcompany y actionid. ¿Por qué no mantener la coherencia y tener el id delante o detrás en ambas?
-
idcompanyyidaction -
companyidyactionid(mi favorita)
Por otro lado, en estas no se separan las palabras, sin embargo en las últimas variables utilizan snake_case. ¿Por qué no algo como?
-
action_idycompany_id
Un nombre polémico
actionid no parece muy representativo. En su propia definición:
Identificador único del evento anonimizando al dispositivo de origen, desde la activación a la desactivación
Quizá habría sido una mejor opción incidence_id o session_id. Algo que represente un poco mejor la idea de que este id debe ser único y constante durante el tiempo que dure la incidencia.
Un endpoint cuestionable
Okey, no hace falta que sea una API RESTful, pero mi comentario viene de aquí:
El método http ya es POST me parece en cierto modo redundante volver a meterlo en el nombre del endpoint (postincidence).
Un dominio poco institucional
Esto ya se ha comentado en algunas fuentes críticas con esta nueva medida, pero sí que es cierto que la API se ha desplegado en un dominio que no parece a primera vista de la DGT:
Sí que es cierto que es un dominio compartido por algunos de los otros servicios de la DGT 3.0, pero quizá no habría estado mal haberlo implementado como subdominio bajo el dominio dgt.es.
Despedida
Y ya estaría. Si te ha parecido interesante el post dale un like y comenta algo. Te aseguro que ha llevado un trabajo escribirlo y me encantará leer y discutir cualquier decisión de diseño. Hasta la próxima 🚀🫶
Recursos y Referencias
Documentación API V-16:
https://github.com/dgt30-esp/Caso-de-uso-1
Especificación Técnica de la baliza V-16















Top comments (0)