Go to file
2025-10-10 01:39:57 +02:00
app fix database schema 2025-10-09 05:34:13 +02:00
assets initial commit 2025-10-08 23:17:23 +02:00
internal add stop and update simulator 2025-10-10 00:29:12 +02:00
.gitignore initial commit 2025-10-08 23:17:23 +02:00
go.mod add querier mock 2025-10-09 14:45:26 +02:00
go.sum add querier mock 2025-10-09 14:45:26 +02:00
Makefile add querier mock 2025-10-09 14:45:26 +02:00
README.md update readme 2025-10-10 01:39:57 +02:00

NATS APP

Lectura de datos de sensores en un dispositivo IoT. Prueba técnica para optar por el puesto de programador Go.

Requisitos previos

  • Docker
  • NATS CLI
  • Make, si prefieres la comodidad de usar Makefile

Comandos

Registrar un sensor

  • Campos obligatorios: sensor_id y sensor_type.
  • Campos opcionales: sampling, thresholdabove y thresholdbelow.

nats req sensors.register '{ "sensor_id": "sensor-001", "sensor_type": "temperature", "sampling": 3600, "thresoldabove": 50.0, "thresoldbelow": -10.0 }'

Actualizar configuración sensor

  • Campo obligatorio: sensor_id y la presencia de al menos un parámetro.

nats req sensors.update '{ "sensor_id": "sensor-001", "sensor_type": "temperature", "sampling": 10, "thresoldabove": 100.0, "thresoldbelow": -15.0 }'

Obtener información de un sensor

  • Campo obligatorio: sensor_id.

nats req sensors.get '{ "sensor_id": "sensor-001" }'

Obtener valores de un sensor

  • Campo obligatorio: sensor_id.
  • Campos opcionales: from y to en formato RFC3339. Si no se especifican, se toman los últimos 7 días.

nats req sensors.values.get '{ "sensor_id": "sensor-001", "from": "2025-10-03T00:00:00Z", "to": "2025-10-10T23:59:59Z" }'

Obtener listado de sensores

No hay payload, pero hay que poner comillas dobles o si no se queda esperando una entrada de datos.

nats req sensors.list ""

Suscribirse a un sensor

nats sub sensors.data.sensor-001

Suscribirse a todos los sensores

nats sub sensors.data.*

Consideraciones

Hay partes de códigos que son snippets extraídos de una librería de autoría propia. Repositorio GitHub. De las cuales son:

  • El logger usando la stdlib log/slog.
  • La conexión con la base de datos, usando el controlador pgx.

Bitácora

Quickstart y toma de contacto con NATS

Lo primero que he hecho es un quickstart del proyecto, con lo que siempre o casi siempre pongo en mis experimentos. Y lo siguiente, en lugar de empezar a construir el proyecto como loco he tratado de entender cómo funciona NATS. Al final ha sido muy sencillo, siguiendo esos pasos:

  1. Levantar el servidor NATS en Docker
  2. Instalar el CLI de NATS
  3. Abrir un puñado de terminales, y en un par de ellas escribir: nats sub "hello" lo cual significa que se está suscribiendo al canal hello. Y en otra escribir: nats pub "hello" "Hola mundo!", lo cual significa que está escribiendo el mensaje Hola mundo! en el canal hello.

demo de NATS

Creación de la aplicación

La parte fácil es hacer todo el boilerplate, hecho en solo commit, que no es lo ideal pero lo dicho, es puro relleno, mucho código ya viene escrito de la librería gopher-toolbox. En este punto el proyecto compila pero hay error en tiempo de ejecución por la falta de implementaciones en el repositorio.

Empezando por el repositorio

El almacenamiento de los datos se ha optado por el uso de TimescaleDB, una extensión de PostgreSQL, el motivo es porque ya he trabajado mucho con ese motor y tengo los drivers ya escritos. Tengo entendido que está optimizado para trabajar con grandes cantidades de datos de series temporales, lo que viene siendo valores de sensores por ejemplo.

Por otro lado también hay un sistema de caché muy rudimentario, en memoria que es un mapa de valores.

Para el registro de valores y mantener ambos se ha usado el patrón decorador que bajo un mismo struct se incluye las dos implementaciones y se llama a ambas funciones. Desde la capa servicios sólo tiene que llamar al decorador sin saber los detalles de la implementación.

Continuamos con los servicios

Con el repositorio sin implementar, se puede realizar los servicios. En ese proyecto ha quedado muy básico, sirviendo solamente de enlace entre los controladores y el repositorio.

Cuando se creó el broker de NATS, inicialmente fue para crear una interfaz de mensajería donde se pudiese manejar websockets, SSE y otras mensajerías pero se consideró que había otras prioridades. Así se quedó.

Y finalmente los controladores

Ahí tuve muchas dudas con el entendimiento de NATS, estaba muy arraigado en el patrón REST, y cambiar la mentalidad costó un poco, al final mirando un poco la documentación me quedé con los conceptos clave:

  1. Está basado en asuntos, los canales se crean de forma jerárquica.
  2. Cuando se hace un subscribe, se suma a un canal del asunto dado.
  3. Para escribir en el canal, hay que hacer el publish.
  4. Finalmente para solicitar un recurso, está el request.

Esto es todo, entonces los controladores de la entidad sensors están constituidos por una serie de endpoints haciendo las acciones que se solicita.

LLMS

He usado Claude para la toma de decisiones y ayuda con el boilerplate, que no es poca cosa, además también se ha usado para la generación de las pruebas unitarias, además de resolución de algunos problemas complejos.

Generadores de código

Existen generadores de código para Golang, de hecho, se fomenta su desarrollo, hay un artículo interesante de Rob Pike hablando sobre ello. Muchas de las herramientas son muy interesantes usarlas ya que acelera mucho la generación de código repetitivo. Da mas confianza usar esas herramientas que la IA.

Para las consultas SQL con seguridad de tipos, existe la herramienta sqlc.

Para ese proyecto sólo se ha usado GoMock, mantenida por Uber y sirve para usar la interfaz Repository sin usar una base de datos real.

Por otro lado, hubiese sido interesante incorporar ginkgo, es un marco de trabajo de pruebas unitarias usando un lenguaje de dominio específico (DSL).

En lugar de escribir una prueba así: Test_Validate(t *testing.T) { ... } se puede hacer de la siguiente manera: var _ Describe("Models", func () { ... }), y dentro del cuerpo se describen las pruebas a realizar.

No se ha incorporado porque hay que instalar la herramienta que ejecutan las pruebas, y no quería correr el riesgo de que no funcionase en otro equipo o no diesen los resultados esperados. Que se podría haber usado un contenedor Docker, sí, pero la prueba no consiste en eso.