Design Google Calendar
Variants:
Google Calendar System Design Requirements
Functional Requirements
View Calendar
Users should be able to view their calendar by day, week, and month.
Create and Update Events
Users should be able to create and update one-time and recurring events.
Invite Other Users
Users should be able to invite other users to meetings, who can then accept or decline.
Receive Notifications
Users should receive notification reminders via email or push notifications.
Non-Functional Requirements
Consistency
The system should be consistent, ensuring that all users see the same view of the calendar.
Robust and Scalable
The system should be robust and scalable to support a large number of users.
CAP Theorem Trade-offs
Trade-off Explanation:
For a calendar application, we prioritize Consistency and Partition Tolerance (CP). It's critical that all users have a consistent view of events, especially when it comes to scheduling and invitations. If there is a network partition, we would rather have the system be temporarily unavailable than to have users see conflicting information.
Scale Estimates
API Design
The API for our calendar service will need to handle event creation, updates, and invitations. Your interviewer will expect you to define a clear and consistent API that allows clients to interact with the core features of the system.
The API will have endpoints for creating calendars, creating events within a calendar, and inviting other users to an event. We'll use a POST
request for each of these operations, as they all result in the creation of a new resource.
Create a new calendar
Create a new event
Invite a user to an event
Database Schema
The database schema for a calendar application needs to handle users, calendars, events, and the relationships between them. A key challenge that your interviewer will expect you to address is how to model recurring events.
We'll use a combination of tables to represent our core entities. The Users
table will store user information. The Calendars
table will store metadata about each calendar. The Events
table will store the template for an event, including its recurrence rule. The EventInstances
table will store the actual occurrences of an event. This separation of Event
and EventInstance
is crucial for efficiently handling recurring events.
Users
Calendars
Event
EventInstance
EventParticipant
High-Level Architecture
The architecture for a calendar service needs to be reliable and scalable, with a clear separation of concerns between the different components. Your interviewer will expect you to be able to break down the system into logical microservices.
Core Services
- User Service: This service is responsible for managing user accounts and their notification preferences.
- Event Service: This is the core of our system. It's responsible for creating, updating, and deleting events and event instances. It also manages event invitations and participant responses.
- Notification Service: This service is responsible for sending event reminders and updates to users. It will be triggered by the Event Notification Scheduler and will use a combination of email, push notifications, and other channels to deliver notifications.
- Event Notification Scheduler: This service is responsible for scheduling and triggering event notifications. It will periodically scan the
EventInstances
table for upcoming events and enqueue notification jobs in an SQS queue.
Deep Dive: Handling Recurring Events
One of the most complex parts of a calendar system is handling recurring events. Your interviewer will want to know how you would model this in your database and how you would efficiently query for event instances.
Pre-compute and Store all Instances
Why: This is the simplest approach, where we generate and store all instances of a recurring event when it's created.
How it works: When a user creates a recurring event, we generate all the EventInstance
records for that event up to a certain point in the future (e.g., 2 years) and store them in the EventInstances
table. When a user views their calendar, we can simply query this table for all instances within a given time range.
Trade-offs:
- Pros: Simple to implement, fast reads.
- Cons: Can lead to a massive number of records for events that recur frequently. Updating or deleting a recurring event requires updating or deleting all of its instances, which can be slow and complex.
Store only the Recurrence Rule
Why: A more scalable approach is to store only the recurrence rule for an event and generate the instances on the fly when a user views their calendar.
How it works: We store the recurrence rule (e.g., "every Tuesday at 2pm") in the Events
table. When a user requests their calendar for a specific time range, we query for all events that could possibly have instances in that range and then use the recurrence rule to generate the instances in memory.
Trade-offs:
- Pros: Uses much less storage, updating a recurring event is a single-row update.
- Cons: Reads are more complex and can be slower, as we have to perform calculations on the fly.
Hybrid Approach for Recurring Events
A hybrid approach is often the best solution. We can pre-compute instances for a certain period in the future (e.g., the next 3 months) and store them in the EventInstances
table. For events further out, we can generate them on the fly. This gives us the best of both worlds: fast reads for the near future, and a scalable solution for events that are far in the future.
Deep Dive: Notifications
A reliable notification system is a critical component of a calendar application. Your interviewer will expect you to discuss how you would design a system that can send millions of notifications per day without delays or failures.
Kafka Queue with a Notification Dispatcher
Why: Using a message queue like Kafka is an excellent way to decouple the notification scheduling from the actual sending of notifications. This makes the system more resilient and scalable.
How it works: When the Event Notification Scheduler determines that a notification needs to be sent, it produces a message to a Kafka topic. This message will contain all the information needed to send the notification, such as the user's contact information, the event details, and the notification type (email, push notification, etc.).
A separate Notification Dispatcher service will consume messages from this Kafka topic. For each message, it will determine the appropriate channel (e.g., email, push notification) and then use a third-party service like SendGrid for email or a push notification service like Apple Push Notification service (APNs) for iOS devices or Firebase Cloud Messaging (FCM) for Android devices.
Trade-offs:
- Pros: Highly scalable and resilient. If the Notification Dispatcher goes down, the messages will remain in the Kafka queue and can be processed when the service comes back online.
- Cons: Adds complexity to the system. Requires managing a Kafka cluster and a separate dispatcher service.
Complete Design
Now that we've covered all the major components individually, let's look at how everything fits together in our complete Google Calendar system design. This diagram shows the end-to-end flow from event creation to notification delivery.
The complete architecture demonstrates how users interact with the Event Service to manage their calendars and events. The Event Notification Scheduler periodically scans for upcoming events and enqueues notification jobs, which are then processed by the Notification Service. This design ensures a reliable, scalable, and feature-rich calendar service.