Skip to main content

Notification & Queue Engine

The central nervous system of Reshma-Core. A hybrid Facade architecture managing persistent In-App alerts (MongoDB) and asynchronous transactional emails (BullMQ/Redis).

BullMQ Redis Nodemailer MJML


Overview

The Notification Module uses the Facade Design Pattern to decouple business logic from messaging infrastructure. It handles two streams:

  1. In-App Notifications – persistent alerts stored in MongoDB, displayed in the user’s dashboard (bell icon).
  2. Email Notifications – asynchronous background jobs (BullMQ/Redis) that compile HTML templates (MJML) and dispatch via SMTP.

All email templates are strictly typed using TypeScript discriminated unions, ensuring compile‑time safety. In‑app notifications are written to MongoDB in a fire‑and‑forget manner to avoid blocking the HTTP response.


Architectural Flow & Performance Optimization

Performance note:

  • Pushing to Redis (BullMQ) is sub‑millisecond.
  • Writing to MongoDB can cause latency spikes, so Notification.create() calls omit await and use .catch() for error logging. The HTTP response returns instantly.

Core Components

1. The Facade Layer (notification.service.ts)

Central entry point for the whole application. Provides specific trigger methods that abstract payload structures.

Key methods:

  • sendOtpEmail() – queues OTP email.
  • triggerWelcome() – hybrid: BullMQ email + async in‑app notification.
  • sendPasswordUpdateConfirmation() – security alert email + in‑app.
  • sendOrderConfirmationNotification() – order confirmation email + in‑app.
  • sendOrderCancelledNotification() – cancellation email with reason.
  • sendOrderShippedNotification() – tracking email + in‑app.
  • sendOrderDeliveredNotification() – delivery email + in‑app.
  • sendReturnRequested/Approved/Rejected/RefundedNotification() – return lifecycle emails + in‑app.
  • sendTicketCreatedNotification() / sendTicketReplyNotification() – support ticket emails + in‑app.
  • sendDataExportEmail() – DPDP/GDPR data export (attached JSON file).

Fire‑and‑forget pattern for in‑app notifications:

Notification.create({ recipientId, type, title, message, link })
.catch(err => logger.error(`In-app notification failed: ${err.message}`));

2. The Presentation Layer (notification.controller.ts)

Only handles in‑app notifications (bell icon). Never dispatches emails.

EndpointMethodDescription
/api/v1/notificationsGETPaginated, unread notifications for the authenticated user.
/api/v1/notifications/:id/readPATCHMarks a notification as read (IDOR‑protected).

3. The Compiler & Strict Payload Validation (compileEmailTemplate)

  • Uses TypeScript discriminated unions (EmailJobPayload). Each job type has its own required data fields.
  • The switch statement is exhaustive: if a new EmailJobType is added but not handled, the TypeScript compiler will error (using the never type).
  • All templates are MJML‑based, compiled to HTML with dark mode support and responsive design.

Exhaustiveness example

switch (payload.type) {
case "OTP_VERIFICATION": return { subject, html: await otpVerificationTemplate(...) };
// ... other cases
default:
const _exhaustiveCheck: never = payload;
throw new Error(`Unhandled email type: ${payload.type}`);
}

Background Queue Architecture (BullMQ)

Producers

QueuePurposeFile
Email QueueAll transactional emailsemail.queue.ts
Data Export QueueDPDP/GDPR takeoutexport.queue.ts
Invoice QueuePDF generationinvoice.queue.ts
Search Sync QueueTypesense eventual consistencyproduct.service.ts

Consumers (Workers)

WorkerPurposeConcurrencyRetry
email.worker.tsCompiles MJML, sends via NodemailerN/A (BullMQ default)3 attempts, exponential backoff
export.worker.tsFetches user data, creates JSON, emails53 attempts
invoice.worker.tsGenerates PDF, uploads to Cloudinary53 attempts (exponential)
Search sync (DLQ)Retries failed Typesense opsN/A10 attempts, exponential backoff

Security: All workers use safeLog() to strip \r\n from logs (CWE‑117 mitigation).


Security & Compliance

ConcernMitigation
IDOR (Insecure Direct Object Reference)Notification.findOneAndUpdate({ _id, recipientId: userId }) ensures ownership.
Log injection (CWE‑117)safeLog() strips control characters before Winston logging.
DPDP/GDPR Right to AccessAsynchronous data export (export.worker.ts) – email with JSON attachment.
DPDP/GDPR Right to be ForgottenAccount deletion triggers deleteUserCart, deleteUserWishlist and anonymises orders.
Rate limitingstandardLimiter applied before authentication on /notifications routes.
Template integrityMJML compilation errors are caught; fallback plain HTML prevents outage.

REST API Specifications (In-App Alerts)

MethodEndpointAccessDescription
GET/api/v1/notificationsProtectedPaginated, unread notifications (newest first). Supports ?page=1&limit=10.
PATCH/api/v1/notifications/:notificationId/readProtectedMarks a single notification as read. Verifies recipientId matches req.user._id.

FilePurpose
src/modules/notifications/notification.controller.tsHTTP layer (in‑app only).
src/modules/notifications/notification.service.tsFacade – dispatches emails and in‑app alerts.
src/modules/notifications/notification.model.tsMongoose schema for in‑app notifications.
src/modules/notifications/interface/email.interface.tsDiscriminated union types for email jobs.
src/shared/queues/email.queue.tsBullMQ producer.
src/shared/queues/email.worker.tsBullMQ consumer – compiles HTML, sends SMTP.
src/shared/queues/export.queue.ts & export.worker.tsData portability.
src/modules/notifications/templates/*.tsMJML email templates (welcome, OTP, order, return, support, etc.).
src/modules/notifications/templates/layout.tsBase MJML layout with dark mode, social links, footer.

See Also


The Reshma-Core Team