node-notification
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
NotificationSchedulingServiceeNotificationRetryService(retry policy configurabili, backoff esponenziale) - Preferenze e quiet hours per utente:
NotificationPreferenceService,UserChannelConfigService,IntelligentTimingper ottimizzare l'orario di consegna - Coda JetStream isolata per tenant (
{namespace}_NOTIFICATIONScon subject{namespace}.notifications.>): processamento asincrono per canale, ack policy esplicita, dead-letter dopoNATS_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.broadcastper 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/):
| Layer | Contenuto |
|---|---|
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-orchestratorinvoca via task il modulo registrato dinode-notificationper inviare un riepilogo email all'utente, con template caricato daTEMPLATE_API_BASE_URLe variabili interpolate - Alert oncall multi-canale: un servizio applicativo pubblica su
services.notification.send.dittaun messaggio concategory: "alert"epriority: 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.broadcastun 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 /notificationsconscheduledAtfuturo;NotificationSchedulingServicela accoda eNotificationRetryServicegestisce eventuali fallimenti di consegna con backoff esponenziale e dead-letter dopo N tentativi
Identità & esposizione
| Campo | Valore |
|---|---|
| Categoria | eventing |
| Versione cluster | 2.0.0 |
| Image | gitea.pzetatouch.it/pzeta_touch/node-notification:1.0.22 |
| URL pubblico | https://ditta.pzeta.it/notification |
| Path regex ingress | `/notification(/ |
| Rewrite a backend | /$2 |
| DNS interno | node-notification-ditta.ditta.svc.cluster.local:3000 |
| Auth nginx | auth_request → node-user-auth |
| Repository | node-notification |
| Endpoint REST | 58 (vedi sezione "API reference") |
Endpoint operazionali
Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:
| Path pubblico | Scopo |
|---|---|
https://ditta.pzeta.it/notification/health | liveness probe |
https://ditta.pzeta.it/notification/ready | readiness probe |
https://ditta.pzeta.it/notification/metrics | metriche Prometheus |
https://ditta.pzeta.it/notification/api-docs.json | spec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true) |
https://ditta.pzeta.it/notification/api-docs | Swagger 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):
| Variabile | Ruolo |
|---|---|
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAME | Connessione PostgreSQL — notifiche persistite, log di consegna, preferenze e account utente |
DATABASE_MAX_CONNECTIONS / _MIN_CONNECTIONS / DB_IDLE_TIMEOUT / DB_CONNECTION_TIMEOUT | Tuning del pool pg |
NATS_URL | URL del cluster NATS; abilita gateway inbound, JetStream per le code e Core NATS per il real-time |
NATS_NAMESPACE | Prefisso applicativo dei subject e nome stream ({namespace}_NOTIFICATIONS); isola i tenant nello stesso cluster |
NATS_JOB_ATTEMPTS / NATS_JOB_BACKOFF_DELAYS / NATS_JOB_RETENTION_HOURS | Politica retry e retention dei job di canale su JetStream |
NATS_WORKER_CONCURRENCY / NATS_WORKER_ACK_WAIT / NATS_WORKER_MAX_DELIVER | Tuning consumer JetStream |
ORCHESTRATOR_ENABLED / ORCHESTRATOR_URL / ORCHESTRATOR_API_KEY / MODULE_ID / MODULE_NAME | Integrazione opzionale con node-orchestrator come modulo registrato |
RENDERING_API_BASE_URL / TEMPLATE_API_BASE_URL | Endpoint del servizio di rendering e del repository dei template |
REDIS_ENABLED / REDIS_HOST / REDIS_PORT | Cache 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_PATH | Endpoint WebSocket diretto (alternativo al pattern Core NATS); disabilitato di default in favore del fan-out NATS |
RATE_LIMIT_MAX / RATE_LIMIT_WINDOW | Rate limiting via @pzeta/fastify-utils |
METRICS_ENABLED / SWAGGER_ENABLED | Espone /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:
- Inbound gateway (consumer di contratto pubblico) —
NATSInboundGatewaysi sottoscrive al subjectservices.notification.send.{namespace}derivato da@pzeta/notification-contracts(buildInboundSubject). I producer (altri microservizi) pubblicano un messaggio conforme ainboundNotificationSchema(Zod) conuserId,category,channelsopzionali,templateCodeocontentinline,priority,correlationId. Il gateway valida, risolve canali e delega aNotificationServiceFacade.sendNotification. Se ichannelsnon sono specificati, l'auto-selezione sceglie il primo canale con account attivo per l'utente. - JetStream interno (consumer + producer privato) —
NATSQueueManagercrea uno stream{namespace}_NOTIFICATIONScon 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 conackWait,maxDelivere DLQ implicita. - 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) tramitepublishRealtime. 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):
frontend Vuenode-orchestrator
Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.