> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bbrands.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Arquitectura

> El dispatcher de notificaciones, el consumidor unificado de la cola, los handlers por canal y el contrato del payload de la cola.

## Componentes

| Componente                      | Ruta                                            |
| ------------------------------- | ----------------------------------------------- |
| Cliente de OneSignal (REST v11) | `lib/integration/onesignal/onesignal.client.ts` |
| Dispatcher (encolado)           | `lib/notification/notification-dispatcher.ts`   |
| Registry `(channel,type)`       | `lib/notification/notification-registry.ts`     |
| Segmentación (`external_id`)    | `lib/notification/notification-targeting.ts`    |
| Ruta del consumidor unificado   | `app/api/queue/notification/route.ts`           |
| Handlers por canal              | `lib/queue/notification/handlers/*.handler.ts`  |

## Contrato del payload de la cola

Cada mensaje en el topic `notification` satisface `NotificationQueuePayload`:

```jsonc theme={null}
{
  "category": "payment",       // marketing | payment | system | transactional
  "channel": "push",           // in_app | live_activity | push | sms
  "correlation": "corr-uuid",  // producer → queue → handler traceability
  "data": { "debtId": "…" },   // channel/type-specific content
  "queue_event_id": "uuid",    // idempotency key
  "type": "PAYMENT_FAILED",    // registry type
  "user": "auth-user-uuid"      // target; used as OneSignal external_id
}
```

El consumidor usa `channel` para segmentar `queue_event.event_type`
(`vercel_queue.notification.<channel>`) y `type` para elegir el handler.

## Uso del dispatcher (productores)

```ts theme={null}
import { notificationDispatcher } from "@/lib/notification/notification-dispatcher";

// Best-effort enqueue: gating + send.
await notificationDispatcher.enqueue({
  category: "payment",
  channel: "push",
  correlation,
  data: { amount, debtId },
  type: "PAYMENT_FAILED",
  user: authUserId,
});

// Durable enqueue (outbox): persists a replayable queue_event before the send.
await notificationDispatcher.enqueueDurable({ /* same params */ });
```

<Note>
  Usa `enqueueDurable` para las notificaciones críticas
  (`payment` / `system` / `transactional`) y `enqueue` para el resto. Los envíos
  masivos usan `enqueueBulk` con throttling por lote.
</Note>

## Primer productor real

El push de pago fallido (`PAYMENT_FAILED`) está conectado de extremo a extremo:
`notifyPaymentFailed` resuelve los miembros de la cuenta y encola un push de `payment`
por usuario cuando un webhook `transaction.failed` de Toku registra un intento fallido.
Como `payment` no es suprimible, el gating de preferencias nunca lo descarta, y
cualquier fallo de encolado se registra sin romper el webhook.

## Agregar un nuevo tipo

<Steps>
  <Step title="Registra el tipo">
    Agrégalo a `NOTIFICATION_TYPES` en `notification-registry.ts`.
  </Step>

  <Step title="Define sus datos">
    Extiende `notification-queue-types.ts` si el tipo necesita sus propios campos.
  </Step>

  <Step title="Override opcional">
    Registra un handler `(channel, type)` para lógica específica del tipo; de lo contrario se usa
    el handler de canal por defecto.
  </Step>

  <Step title="Produce">
    Llama a `notificationDispatcher.enqueue(...)` desde el servicio de negocio.
  </Step>
</Steps>

No se necesita ningún cambio en la ruta del consumidor ni en `vercel.json` para un nuevo tipo
dentro de un canal ya soportado.
