# ADR-0002: Avisar por WhatsApp con una tabla de avisos pendientes

## Estado

Propuesta (ejemplo del apunte de la semana 7).

## Contexto

Hoy Kallpa "avisa" escribiendo en la consola: `consoleNotifier` implementa el
puerto `Notifier`. El complejo va a contratar WhatsApp Business y el aviso tiene
que salir de verdad, por una llamada HTTP a un proveedor externo que puede
tardar o caerse.

Las fuerzas en juego:

- El aviso es un complemento: el vecino necesita la reserva, y la ve en la página
  aunque el mensaje no llegue. Un aviso caído no debe impedir reservar.
- El servidor procesa cada reserva sin ceder el control entre revisar que la hora
  esté libre y guardarla. Esa propiedad evita los cruces en un solo proceso, y un
  `await` a un proveedor externo en ese tramo la rompería.
- El equipo son dos personas a tiempo parcial y el complejo no paga más servidores.
  Kallpa recibe unas decenas de reservas al día.

Escenario de calidad: *con el proveedor de WhatsApp caído durante 10 minutos, en
operación normal, el 99 % de las reservas responde en menos de 500 ms y, cuando
el proveedor vuelve, cada aviso sale en menos de 2 minutos.*

## Decisión

Vamos a guardar cada aviso en una tabla `pending_notifications`, en la misma
transacción que la reserva. Un temporizador dentro del mismo proceso del servidor
la revisa cada 30 segundos, llama al proveedor y marca lo que salió.

## Alternativas consideradas

### Llamar al proveedor dentro de la petición

**A favor.** Es lo más simple: un adaptador nuevo del puerto `Notifier` y nada más.
El vecino sabe enseguida si el aviso salió.

**En contra.** Si el proveedor tarda, tarda la reserva; si se cae, hay que decidir
entre fallar la reserva o perder el aviso. No cumple el escenario. Además, el
puerto `Notifier` es síncrono: hacerlo asíncrono invita a poner un `await` en el
handler.

### Tabla de avisos pendientes y temporizador en el mismo proceso (elegida)

**A favor.** Cumple el escenario. La reserva y su aviso se guardan juntos: si el
proceso se cae, el aviso sigue pendiente. No agrega piezas para desplegar ni
operar. Se prueba con un reloj fijo y un doble del proveedor.

**En contra.** El aviso sale hasta 30 segundos después. Si el proceso se cae entre
llamar al proveedor y marcar el aviso, se repite al volver: un vecino puede recibir
dos veces el mismo mensaje. Hay que escribir y mantener el temporizador.

### Una cola de mensajes externa y un proceso aparte que envía

**A favor.** Es la forma habitual cuando hay mucho volumen o varios consumidores.
El envío escala y se despliega por separado del servidor.

**En contra.** Son dos piezas más que desplegar, vigilar y pagar, para unas decenas
de mensajes al día. La reserva y el mensaje ya no se guardan en una sola
transacción, así que aparece el caso de "se guardó la reserva, pero no el mensaje".
Nadie del equipo ha operado una cola.

## Consecuencias

- Lo que se gana: reservar deja de depender del proveedor, y cambiar de proveedor
  toca un solo adaptador.
- Lo que se pierde: el aviso ya no es inmediato, y puede llegar repetido. Lo
  aceptamos porque un mensaje repetido molesta menos que uno perdido. Hay que
  decírselo al complejo.
- Lo que ahora hay que hacer: una migración para la tabla, el temporizador y un
  límite de reintentos, para que un aviso imposible no se reintente para siempre.
- Deuda que queda, a propósito: el temporizador supone una sola instancia. Si dos
  instancias revisan la misma tabla, los dos pueden mandar el mismo aviso. Si el
  complejo decide correr dos instancias (ver ADR-0001), hay que revisar esta
  decisión.
- Cuándo revisarla: si el volumen pasa de unos cientos de avisos al día, o si otro
  sistema también necesita enterarse de las reservas.
