Design Twitter's core functionality — users post tweets, follow other users, and see a timeline feed showing tweets from people they follow. The system must handle celebrities with millions of followers.
[Write Path]
User → API Gateway → Tweet Service
│
Kafka (tweet events)
│
┌───────────┴────────────┐
│ │
Fan-out Service Search Indexer
│ (Elasticsearch)
injects tweet into
followers' timeline caches
[Read Path]
User → API Gateway → Timeline Service → Redis Cache → DB fallback
Fan-out on write (push model): when a tweet is posted, a background worker writes it to all followers' timeline caches. Timeline reads are O(1) from Redis.
POST /api/v1/tweets
Body: { text, mediaIds? }
Response: { tweetId, createdAt }
GET /api/v1/timeline/home?cursor=&limit=20
Response: { tweets: [...], nextCursor }
GET /api/v1/tweets/{tweetId}
POST /api/v1/users/{userId}/follow
DELETE /api/v1/users/{userId}/follow
GET /api/v1/search?q=keyword&cursor=
Tweets (Cassandra — append-only, time-series)
tweets
tweet_id UUID PK
user_id UUID
content TEXT
media_urls LIST<TEXT>
created_at TIMESTAMP
like_count COUNTER
User social graph (dedicated graph store or Cassandra)
followers
user_id UUID PK
follower_id UUID
following
user_id UUID PK
followee_id UUID
Timeline cache (Redis Sorted Set, score = tweet timestamp)
timeline:{userId} → ZSet of {tweetId: timestamp}
Fan-out on write breaks for celebrities (e.g., Obama with 130M followers). Writing to 130M timeline caches per tweet takes ~10 seconds.
Solution — Hybrid fan-out:
At read time, the timeline service merges:
Retain only the 800 most recent tweet IDs per user. Older tweets loaded from DB on scroll.