DEV Community

Shalini Goyall
Shalini Goyall

Posted on

System Design : Calendar App

Functional Requirement

  • create event , modify event , cancel event
  • user should be able to view calendar daily , weekly or yearly
  • user should be able to set tup recurring meeting
  • send notification for any change via email.

Non functional requirement

  • High availability >> consistency ( eventaul consitency for syncing events)
  • should support 1B User
  • low latency to view calendar.
  • read >> write

Models

  • User
  • event
  • Recurrence

APIs

  • POST /events/
{
   title 
   userId ( creator)
   userIds []
   start time
   end time
   json blob content - > video call link , description
   - recurrence schedule > weekly , biweekly , monhtly or yearly
}
Enter fullscreen mode Exit fullscreen mode
  • GET / events/startDay=?&&endDay=? return -> List of events

High Level Design

Deep Dives

1. How to store data into DB for daily or recurring events. Explain it with example.

  1. Event Creation & Storage

Case 1: Simple (One-Time) Event

Example

Doctor Appointment
Jan 10, 3:00–4:00 PM (UTC)

events table (single row)

| event_id | owner_id | title              | start_time_utc   | end_time_utc     | rrule | tz  | version |
| -------- | -------- | ------------------ | ---------------- | ---------------- | ----- | --- | ------- |
| evt_101  | user_1   | Doctor Appointment | 2025-01-10 15:00 | 2025-01-10 16:00 | NULL  | UTC | 1       |
Enter fullscreen mode Exit fullscreen mode

GET Behavior

  • Row is returned
  • Rendered directly
  • No expansion needed

Case 2: Recurring Event (Weekly)

Example

Team Sync
Every Monday, 10–11 AM
Starting Jan 6, 2025

`events` table (still ONE row)

| event_id | owner_id | title     | start_time_utc   | end_time_utc     | rrule                | tz  | version |
| -------- | -------- | --------- | ---------------- | ---------------- | -------------------- | --- | ------- |
| evt_201  | user_1   | Team Sync | 2025-01-06 10:00 | 2025-01-06 11:00 | FREQ=WEEKLY;BYDAY=MO | UTC | 1       |
Enter fullscreen mode Exit fullscreen mode

GET Behavior (Week View)

  • Fetch base row
  • Expand rule only for requested range
  • Generate occurrences in memory

Case 3: Recurring Event with Exception (One Cancellation)

Example

Team Sync
Cancelled only on Jan 20

event_exceptions` table

| exception_id | event_id | exception_date | type      |
| ------------ | -------- | -------------- | --------- |
| ex_301       | evt_201  | 2025-01-20     | CANCELLED |
Enter fullscreen mode Exit fullscreen mode

GET Behavior (Week of Jan 20)

  1. Expand weekly rule → Jan 20 occurrence
  2. Match exception → drop occurrence
  3. Return remaining events

Why This Storage Model Scales

Scenario Rows Stored
One-time event 1
Weekly for years 1
Weekly + N exceptions 1 + N

✅ Minimal storage
✅ Fast queries
✅ No occurrence explosion

2. View generation and low latency

User flow :
- Client fetch data from server.
- Server fetch data from DB.
- Server expands events for a time range

  • Server send list of events to client

Optimisation for low latency to get result :

  1. Redis cache : Server can push the events to Redis. When user wants to see the events in couple of hours, it made request to server. server first look into redis and serve. If events are not present , then server made call to DB, fetch events from DB and push to client and redis.

Pros :
- Simple client - no extra logic on client side.
- Consistent shared views

Cons
- Redis explosion at scale
- Hard cache invalidation
- Per-user views don’t reuse well
- Expensive at 1B users

Conclusion:
❌ Not ideal for massive scale.

  1. Client side expansion + SQLite (Hybrid)

User Flow
- User made request to Server.
- Server sends events rules , exceptions to client.
- Client expands events to multiple events
- Client saves occurrences into SQL lite DB.

Pros
- Extremely fast UI
- Offline support
- No server-side expanded cache
- Scales naturally with users

Cons
- More complex client
- Server must manage sync carefully

Conclusion:
✅ Preferred at very large scale.

3. Conflict Detection and Locking Strategy

  • Optimistic Locking (Default)

    • low contention
    • best of personal calendar
    • retry logic should be there in service.
  • Pessimistic Locking

    • High contention calendars to avoid race condition.
    • Lock many rows.

4. Multi-Device Sync ( push and pull)

  • Pull (Delta Sync) Used for:
    • Cold start
    • Reconnect
    • Missed updates
  • Push (SSE / Push Notifications)

    • Fetches updated rule
    • Re-expands locally
    • Updates SQLite + UI

    SSE preferred:

    • One-way
    • Lightweight
    • Battery friendly

    Hybrid model -> Push for freshness, pull for correctness.

5. Database Choice: SQL vs NoSQL

Why SQL works well:
- Strong consistency
- Transactions
- Overlap queries
- Recurrence rules stored efficiently

This is not write-heavy, so SQL scales well.

No SQL tradeoffs

Issue Explanation
Conflict checks Hard without transactions
Recurrence queries Poor fit
Consistency Eventual by default.
Complexity Higher for correctness

Top comments (0)