Functional Requirements:
Non-Functional Requirements:
202 Accepted immediately, decoupling the internal system from external provider latencies.Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
1. Trigger a Notification POST /v1/notifications/send
JSON
{
"notification_id": "uuid-9876",
"user_id": "user-456",
"template_id": "order_shipped_alert",
"channels": ["PUSH", "SMS"],
"dynamic_data": {
"order_number": "12345",
"delivery_time": "Tomorrow at 2 PM"
}
}
Returns: 202 Accepted. The API validates the payload, drops it into the initial Kafka/RabbitMQ ingestion topic, and returns immediately.
incoming_notifications message broker.Here are the refined mechanics of the resilient delivery pipeline.
1. Idempotency & Rate Limiting (Redis)
Before the Ingestion Service puts a message on the queue, it checks Redis for the notification_id. If it exists, the request is dropped as a duplicate. Next, it increments a rate-limiting counter for that user_id. If the user has received > 10 notifications in the last hour, the message is either dropped or shunted to a low-priority digest queue.
2. The Router & Fan-Out Process
Router Workers pull from incoming_notifications.
fcm_queue, apns_queue, sms_queue).3. Resilient Sending & Exponential Backoff
Sender Workers pull from their respective provider queues and execute the HTTP calls.
analytics_queue.Retry Queue. Crucially, it applies Exponential Backoff with Jitter (e.g., wait 2s, then 5s, then 12s, plus a random millisecond offset) to ensure our retry workers don't accidentally DDoS the external provider when it comes back online.The data storage is split to handle configuration versus high-volume logging.
1. The Configuration Store: PostgreSQL (Users & Devices)
Designed for strong consistency to ensure strict adherence to user privacy and opt-outs.
| Column Name | Data Type | Constraints/Indexes | Description |
user_id | UUID | Primary Key | Identifies the user. |
channel_type | ENUM | Composite PK | APNS, FCM, SMS, EMAIL. |
destination | VARCHAR | Indexed | The actual device token, phone number, or email. |
is_active | BOOLEAN | - | Set to FALSE automatically if FCM/APNs reports it as dead. |
opt_in_marketing | BOOLEAN | - | Checked by the Router Worker before sending. |
2. The Delivery Log Store: Cassandra / MongoDB (Analytics)
Designed to absorb the massive write-heavy firehose of delivery receipts, opens, and clicks without slowing down the core sending pipeline.
| Column Name | Data Type | Partition/Clustering | Description |
notification_id | UUID | Partition Key | Ties logs back to the original API request. |
timestamp | Timestamp | Clustering Key | Sorts events chronologically. |
user_id | UUID | Indexed | For querying a user's notification history. |
status | VARCHAR | - | SENT, DELIVERED, OPENED, BOUNCED. |
provider_response | JSON | - | The raw error message from Twilio/Apple for debugging. |