Build one central notification service. It checks user preferences, renders templates, and puts one message per channel on a queue. Workers per channel then send through the providers.
API: internal services call the notification service, or publish an event. The request holds:
- the user
- a template id and the data for it
- the channels
- a priority
- an idempotency key: a unique id for this request, so the same request is never sent twice
Processing: the service does these steps:
- It validates the request.
- It loads the user preferences and opt-outs. An opt-out means the user said "don't send me this".
- It checks quiet hours and per-user frequency caps (a limit on how many messages one user gets).
- It renders the template with the data.
- It stores a notification record with status
pending. - It puts one message per channel on separate queues: push, email and SMS.
Use priority separation: urgent messages get their own queue. Why? An OTP (one-time password) code must never wait behind a big marketing send.
Workers: each channel has its own workers that call the providers:
- APNs or FCM for push
- an email provider for email
- an SMS gateway for SMS
The workers respect provider rate limits. They retry temporary failures with backoff, which means a longer wait before each retry. When a message runs out of retries, they send it to a DLQ (dead-letter queue: a queue that holds failed messages for later checking). Workers dedupe on the idempotency key, so a retry never sends the same message twice.
Tracking: providers call back with a status, such as delivered or bounced. The service updates the record and shows this status to the support team. Scheduled notifications go to a delayed queue or a scheduler.
Scaling: add more workers per channel. The queue holds sudden bursts until the workers catch up. For critical channels, add provider failover: if one provider is down, switch to another. This improves availability.
- A central service handles preferences, templates and opt-outs.
- Each channel has its own queue, and urgent messages are kept apart from bulk ones.
- Workers retry with backoff, respect provider rate limits, and send failed messages to a DLQ.
- An idempotency key stops the same notification from being sent twice.
- The service tracks delivery status and supports scheduled notifications.