node-notification

Notifiche utente e canali pub/sub interni.
eventing
/notificationauth: nginx

node-notification

In sintesi

Hub centralizzato delle notifiche del tenant: riceve richieste di delivery da altri microservizi (via REST o via subject NATS services.notification.send) e le instrada su tredici canali (email SMTP/Resend, SMS Twilio, push mobile FCM, web-push browser, in-app persistito su PostgreSQL, Telegram, webhook generico, Mattermost, Slack, Teams, WhatsApp). Gestisce template, preferenze utente, account multi-canale, scheduling e retry, e ripubblica le notifiche in-app in real-time via Core NATS verso il frontend Vue connesso.

Funzionalità principali

  • Delivery multi-canale con provider adapter dedicati per ogni canale: email (SMTP/Resend), SMS (Twilio), push (Firebase Cloud Messaging), web-push (VAPID), in-app, Telegram, webhook, Mattermost, Slack, Teams, WhatsApp
  • Auto-selezione canale quando il chiamante non specifica channels: il gateway interroga gli account configurati dell'utente e sceglie il primo canale attivo seguendo l'ordine email > sms > in_app > telegram > push > webhook, con fallback finale in-app
  • Template engine con variabili interpolate, escape HTML opzionale e caching layered (EnhancedTemplateCacheService); i template sono caricati da repository esterno e validati
  • Scheduling e retry delle notifiche con NotificationSchedulingService e NotificationRetryService (retry policy configurabili, backoff esponenziale)
  • Preferenze e quiet hours per utente: NotificationPreferenceService, UserChannelConfigService, IntelligentTiming per ottimizzare l'orario di consegna
  • Coda JetStream isolata per tenant ({namespace}_NOTIFICATIONS con subject {namespace}.notifications.>): processamento asincrono per canale, ack policy esplicita, dead-letter dopo NATS_WORKER_MAX_DELIVER
  • Pub/sub real-time via Core NATS: notifiche in-app ripubblicate su {namespace}.notifications.inapp.user.{userId} e broadcast su {namespace}.notifications.inapp.broadcast per consegna best-effort al browser
  • Integrazione orchestrator: registra task come modulo @pzeta/orchestrator-node, esponendo le proprie capability di invio come step richiamabili da workflow
  • Analytics di delivery e di coda (AnalyticsService, ChannelAnalyticsApplicationService, @pzeta/queue-analytics), provider health monitoring, log di consegna persistito

Architettura

Stack: Fastify v5 · Inversify (DI con constructor injection) · PostgreSQL via driver pg (no ORM) · NATS 2.x con JetStream · @pzeta/notification-contracts per i contratti inbound · @pzeta/orchestrator-node per l'integrazione con node-orchestrator · @pzeta/fastify-utils (auth, security, errorHandler, healthcheck, openapi, metrics) · @pzeta/log per logging strutturato · Zod per validazione · provider SDK: nodemailer, resend, twilio, firebase-admin, web-push.

Layout DDD (src/):

LayerContenuto
domain/Notification (aggregate root), value object (NotificationType, NotificationChannel, NotificationStatus, QuietHours, IntelligentTiming, DeliveryAttempt), specifications, strategies di routing, eventi e factory
application/Servizi orchestrali decomposti per SRP — NotificationServiceFacade come backward-compatible entrypoint, e i servizi specializzati NotificationCreationService, NotificationSendingService, NotificationSchedulingService, NotificationRetryService, NotificationDispatcher, InAppNotificationService, NotificationPreferenceService, UserChannelConfigService, ProviderRegistry, TemplateLoaderService, AnalyticsService
infrastructure/ConnectionPool PostgreSQL, repository (PostgresNotificheRepository, PostgresDeliveryLogRepository, PostgresNotificationAccountRepository, PostgresNotificationPreferenceRepository, PostgresUserChannelConfigRepository), NATSQueueManager + NATSInboundGateway, TemplateEngine, OrchestratorAdapter, provider concreti (SMTPProvider, ResendProvider, TwilioProvider, FCMProvider, WebPushProvider, InAppProvider, TelegramProvider, WebhookProvider, MattermostProvider, SlackProvider, TeamsProvider, WhatsAppProvider), cache multi-layer, migration runner
presentation/Controller, route plugin (NotificationServiceFacadeRoutes, InAppNotificationRoutes, AnalyticsRoutes, ProviderHealthRoutes, CacheUnifiedRoutes, ConfigChannelRoutes, ConfigAccountRoutes, ConfigPreferenceRoutes, UserChannelConfigRoutes, ConfigProviderSchemaRoutes), schemi Zod, middleware

Pattern adottati: Facade (NotificationServiceFacade aggrega i servizi specializzati), Strategy (selezione provider per canale), Dispatcher (NotificationDispatcher separa la scelta del provider dal flusso applicativo), Repository, Circuit Breaker sui provider esterni, Adapter (un provider per ciascun SDK), DataLoader batching per i template, cache layered (templates / preferenze / provider config / metadata / quick-lookup) con eviction policy configurabile.

Bootstrap: AppBootstrapper esegue in ordine connessione DB, migrazioni schema all'avvio, registrazione servizi nel container Inversify, middleware, security + auth nginx, healthcheck + metrics + OpenAPI, inizializzazione dell'adapter orchestrator (se ORCHESTRATOR_ENABLED=true), avvio del NATSInboundGateway (se NATS_URL configurato), registrazione route e graceful shutdown.

Casi d'uso

  • Notifica di completamento workflow: un workflow di node-orchestrator invoca via task il modulo registrato di node-notification per inviare un riepilogo email all'utente, con template caricato da TEMPLATE_API_BASE_URL e variabili interpolate
  • Alert oncall multi-canale: un servizio applicativo pubblica su services.notification.send.ditta un messaggio con category: "alert" e priority: 9; il gateway risolve i canali email + SMS + Telegram dell'utente target e li attiva in parallelo con escalation
  • Broadcast tenant: un'operazione amministrativa pubblica su {namespace}.notifications.inapp.broadcast un messaggio che tutte le sessioni Vue connesse via NATS-WebSocket ricevono e mostrano in tempo reale
  • Notifica utente cross-tab via Core NATS: dopo aver persistito una notifica in-app su PostgreSQL, il servizio chiama publishUserNotification(userId, ...) su {namespace}.notifications.inapp.user.{userId}; tutte le tab del browser dell'utente la ricevono e aggiornano il contatore senza polling
  • Reminder schedulato: un client REST chiama POST /notifications con scheduledAt futuro; NotificationSchedulingService la accoda e NotificationRetryService gestisce eventuali fallimenti di consegna con backoff esponenziale e dead-letter dopo N tentativi

Identità & esposizione

CampoValore
Categoriaeventing
Versione cluster2.0.0
Imagegitea.pzetatouch.it/pzeta_touch/node-notification:1.0.22
URL pubblicohttps://ditta.pzeta.it/notification
Path regex ingress`/notification(/
Rewrite a backend/$2
DNS internonode-notification-ditta.ditta.svc.cluster.local:3000
Auth nginxauth_requestnode-user-auth
Repositorynode-notification
Endpoint REST58 (vedi sezione "API reference")

Endpoint operazionali

Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:

Path pubblicoScopo
https://ditta.pzeta.it/notification/healthliveness probe
https://ditta.pzeta.it/notification/readyreadiness probe
https://ditta.pzeta.it/notification/metricsmetriche Prometheus
https://ditta.pzeta.it/notification/api-docs.jsonspec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true)
https://ditta.pzeta.it/notification/api-docsSwagger UI (solo in NODE_ENV !== production)

Configurazione

Variabili d'ambiente che un integratore deve conoscere (per la lista completa vedi EnvironmentPlugin e .env.example del repo):

VariabileRuolo
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAMEConnessione PostgreSQL — notifiche persistite, log di consegna, preferenze e account utente
DATABASE_MAX_CONNECTIONS / _MIN_CONNECTIONS / DB_IDLE_TIMEOUT / DB_CONNECTION_TIMEOUTTuning del pool pg
NATS_URLURL del cluster NATS; abilita gateway inbound, JetStream per le code e Core NATS per il real-time
NATS_NAMESPACEPrefisso applicativo dei subject e nome stream ({namespace}_NOTIFICATIONS); isola i tenant nello stesso cluster
NATS_JOB_ATTEMPTS / NATS_JOB_BACKOFF_DELAYS / NATS_JOB_RETENTION_HOURSPolitica retry e retention dei job di canale su JetStream
NATS_WORKER_CONCURRENCY / NATS_WORKER_ACK_WAIT / NATS_WORKER_MAX_DELIVERTuning consumer JetStream
ORCHESTRATOR_ENABLED / ORCHESTRATOR_URL / ORCHESTRATOR_API_KEY / MODULE_ID / MODULE_NAMEIntegrazione opzionale con node-orchestrator come modulo registrato
RENDERING_API_BASE_URL / TEMPLATE_API_BASE_URLEndpoint del servizio di rendering e del repository dei template
REDIS_ENABLED / REDIS_HOST / REDIS_PORTCache distribuita opzionale (in alternativa la cache è in-memory)
CACHE_ENABLED e variabili *_CACHE_*Abilitazione e dimensionamento dei layer di cache (templates, preferences, providerConfig, metadata, quickLookup)
Provider canale: SMTP_*, RESEND_API_KEY, TWILIO_*, FCM_* (Firebase service account), VAPID_* (web-push), TELEGRAM_BOT_TOKEN, SLACK_*, MATTERMOST_*, TEAMS_*, WHATSAPP_*Credenziali dei provider esterni; un canale è disponibile solo se il provider corrispondente è configurato e validato da ProviderValidationService
WEBSOCKET_ENABLED / WEBSOCKET_PORT / WEBSOCKET_PATHEndpoint WebSocket diretto (alternativo al pattern Core NATS); disabilitato di default in favore del fan-out NATS
RATE_LIMIT_MAX / RATE_LIMIT_WINDOWRate limiting via @pzeta/fastify-utils
METRICS_ENABLED / SWAGGER_ENABLEDEspone /metrics Prometheus e Swagger UI

Il servizio esegue le migrazioni dello schema all'avvio (MigrationService.runMigrations): un deploy con codice nuovo aggiorna automaticamente la base dati.

Note eventing NATS

Il servizio agisce sia come consumer che come producer sul cluster NATS, con tre pattern distinti:

  1. Inbound gateway (consumer di contratto pubblico)NATSInboundGateway si sottoscrive al subject services.notification.send.{namespace} derivato da @pzeta/notification-contracts (buildInboundSubject). I producer (altri microservizi) pubblicano un messaggio conforme a inboundNotificationSchema (Zod) con userId, category, channels opzionali, templateCode o content inline, priority, correlationId. Il gateway valida, risolve canali e delega a NotificationServiceFacade.sendNotification. Se i channels non sono specificati, l'auto-selezione sceglie il primo canale con account attivo per l'utente.
  2. JetStream interno (consumer + producer privato)NATSQueueManager crea uno stream {namespace}_NOTIFICATIONS con subject {namespace}.notifications.{channel}[.urgent|.normal]. Il servizio pubblica il job sul subject del canale (jetStreamClient.publish) e contestualmente è il consumer durable che processa il job invocando il provider corrispondente. Delivery at-least-once con ackWait, maxDeliver e DLQ implicita.
  3. Core NATS real-time (producer fire-and-forget) — Le notifiche in-app, dopo la persistenza, vengono ripubblicate su {namespace}.notifications.inapp.user.{userId} (per il singolo utente) o {namespace}.notifications.inapp.broadcast (per tutti i connessi) tramite publishRealtime. Il browser Vue connesso via NATS-WebSocket riceve l'aggiornamento senza polling; se nessun subscriber è connesso il messaggio viene scartato silenziosamente perché la notifica è già persistita in PostgreSQL.

Quando NATS_URL non è configurato il servizio funziona comunque via REST e via integrazione orchestrator: i pattern (1) e (3) sono disabilitati con log warning, e i job sono processati sincronicamente dal NotificationDispatcher.

Dipendenze e dipendenti

Dipende da (servizi che questo servizio chiama):

Consumato da (chi chiama questo servizio):

Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.

Loading OpenAPI…
Loading NATS contracts…