the content of the notification can be registered in 3 different languages. Let's assume the content is 1024 characters for each notification.
Notification content: language - 4 byte, 1024 content, created date -8byte, created userId - 8 bytes => ~1280 bytes = 1,4 KB per content -> 3(language) * 100 * 1,4 KB=> 420KB daily content ~= 0.5 MB daily
sending statistics: contentId(8 bytes) contactId(8 bytes), userdate(8 bytes), bool opened, bool receivde ~= 50 bytes * 1 billion => 50GB daily.
User preferences for 1000 customers with 1 billion contacts(contactId, optin
Throughput estimation :1 billion contacts receive notification => 1,4 KB * 10000/s => 14000 KB/s => 15MB/s
POST /content (content, language)-> register content for a language
Get /content -> get contents, returns a list of contents
POST /segment -> create a segment of contacts, returns the number of matching contacts
POST /content/{id}/schedule(segment_Id, date, content) schedule the content to be sent
POST /content/{id}/track -> track the content opening rates and reading
POST /notification -> send transactional notification
Reading path: is really easy can be achieved with any database
Write path: send to contacts -> 10 000 writes per sec for sending which can be batched, but tracking is write heavy.
We can use a hybrid approach using
Content creation: customer apps will post the content in the system. The system will be hit in the Api Gateway where the request is routed to the Content Service and stored in SQL database. The GET used to retrieve the content will follow the same path.
Scheduling content: Customer apps will schedule the content created before for a segment of users using the Api. ApiGateway will route the requests to Scheduling Service that will store the data into SQL database.
A chron job will determine if there is content to be sent to the clients by inspecting scheduled content. If there is any content, the content and the user data will be put on a queue to be manipulated by the Notification Service.
Upon receiving the data the Notification Service will contact WebSockets manager to send the content using the connection opened and stored for the client.
Client app upon receiving will report back the content was received for the user using the same opened connection
Scheduling Service will determine users of the segment by using an lazy loading approach, the users that will receive the notification will be queries if they match the criteria at the time when they need to be sent. The Outbox pattern with pagination can be used here to add the users and content to the queue. In case the system fails in any step, can reload the process from the step/page it remained.
Notification Service and Websockets manager, clients apps will use exponential backoff to try to reconnect in case the connection has failed.
The notification service will pick the messages from queue, will fill the data with the specific user data, and will use Websockets manager to push the notifications to the client. Websockets manager will determine on which instance the user is connected and use the websocket channel to notify the user.
The tracking service consists of an API over Websockets receiving statistics which content reached the client, if the user opened the content, the statistics is saved in the NoSQL for fast storing, the data can then be sent to statistics database time based DB Prometheus and use Grafana for showing Dashboards
For message queues we can use Kafka to handle the notification efficiently. Also Kafka can be used to send events to the Analytics module.
Websockets Manager is used to manage the websocket connections of the user. Websockets manager is separated from other logic so scaling the websockets servers gets easier. The notifications can use a custom logic router from Websockets manager which will determine where each client is connected (maybe using Zookeeper) and using techniques like Consistent Hashing to handle connections or servers failures.
For scaling the system a Load Balancer can used to distribute the requests towards instances.
A database like Cassandra offers already sharding /partitioning of data
Fault tolerance for the database can be used use write and read replicas, and too choose another leader in case of failure.
Another weak point is unstable websocket connection of the user with the Websocket Manager resulting in connection and reconnection of the user with different servers, an threshold time, and retries can used to retry to send the notification
Nice dashboards can be implemented of the notifications data and tracking data.