Real-world Scenarios

Practice Data Modeling

Design Cassandra schemas for real-world applications!

Scenario 1
Social Media
📱 Twitter-like Social Network
You're building a Twitter-like application where users can post tweets, follow other users, and see their personalized timeline. Design the data model to support high-volume reads and writes.
Required Queries
  • Q1: Get user profile by username
  • Q2: Get all tweets by a specific user (most recent first)
  • Q3: Get user's timeline (tweets from people they follow)
  • Q4: Get list of users someone is following
  • Q5: Get follower count for a user
💡 Modeling Considerations
  • Tweets should be time-ordered (newest first)
  • Timeline queries must be fast (< 50ms)
  • Users can have millions of followers
  • Consider denormalization for performance
Data Model Solution

📋 Table 1: User Profiles

CREATE TABLE users ( username text PRIMARY KEY, user_id uuid, display_name text, bio text, created_at timestamp, follower_count counter, following_count counter );

Why: Username is the partition key for fast user lookups. Counters track followers/following efficiently.

📋 Table 2: User Tweets

CREATE TABLE tweets_by_user ( username text, tweet_id timeuuid, content text, likes_count int, retweets_count int, PRIMARY KEY (username, tweet_id) ) WITH CLUSTERING ORDER BY (tweet_id DESC);

Why: Partition by username, cluster by timeuuid DESC for reverse chronological order. Q2 ✓

📋 Table 3: User Timeline (Denormalized)

CREATE TABLE timeline ( username text, -- The viewer tweet_id timeuuid, author_username text, -- Who posted it content text, posted_at timestamp, PRIMARY KEY (username, tweet_id) ) WITH CLUSTERING ORDER BY (tweet_id DESC);

Why: Pre-computed timeline! When someone tweets, fan-out to all followers' timelines. Fast reads, more writes. Q3 ✓

📋 Table 4: Following Relationships

CREATE TABLE following ( follower_username text, following_username text, followed_at timestamp, PRIMARY KEY (follower_username, following_username) );

Why: Who is user X following? Partition by follower. Q4 ✓

🎯 Key Design Decisions
  • Denormalization: Timeline table duplicates tweet data for fast reads
  • Time-ordering: Use timeuuid for natural time-based sorting
  • Write amplification: Fan-out writes to followers' timelines
  • Counters: Track followers/following without read-modify-write
  • Query-first: Each table designed for specific query pattern
Scenario 2
E-commerce
🛒 Online Shopping Platform
Design a data model for an e-commerce platform that handles products, orders, and user shopping carts. Support product browsing, order history, and real-time inventory.
Required Queries
  • Q1: Get product details by product_id
  • Q2: Get all products in a category
  • Q3: Get user's order history (most recent first)
  • Q4: Get order details including all items
  • Q5: Get user's current shopping cart
💡 Modeling Considerations
  • Products can belong to multiple categories
  • Orders should include snapshot of product data
  • Shopping cart must support concurrent updates
  • Fast product browsing is critical
Data Model Solution

📋 Table 1: Products

CREATE TABLE products ( product_id uuid PRIMARY KEY, name text, description text, price decimal, stock_quantity int, categories set<text>, images list<text> );

Why: Simple lookup by product_id. Use collections for categories and images. Q1 ✓

📋 Table 2: Products by Category

CREATE TABLE products_by_category ( category text, product_id uuid, name text, price decimal, image_url text, PRIMARY KEY (category, product_id) );

Why: Browse products by category. Denormalized for fast category pages. Q2 ✓

📋 Table 3: Orders by User

CREATE TABLE orders_by_user ( user_id uuid, order_id timeuuid, order_date timestamp, total_amount decimal, status text, PRIMARY KEY (user_id, order_id) ) WITH CLUSTERING ORDER BY (order_id DESC);

Why: Order history per user, newest first. Q3 ✓

📋 Table 4: Order Details

CREATE TABLE order_items ( order_id timeuuid PRIMARY KEY, user_id uuid, order_date timestamp, items list<frozen<order_item>>, subtotal decimal, tax decimal, shipping decimal, total decimal ); -- UDT for order items CREATE TYPE order_item ( product_id uuid, product_name text, price decimal, quantity int );

Why: Complete order with items as UDT. Snapshot product data at purchase time. Q4 ✓

📋 Table 5: Shopping Cart

CREATE TABLE shopping_carts ( user_id uuid, product_id uuid, quantity int, added_at timestamp, PRIMARY KEY (user_id, product_id) );

Why: One row per cart item. Easy to add/update/remove. Q5 ✓

🎯 Key Design Decisions
  • Denormalization: products_by_category duplicates for fast browsing
  • Collections: Use set for categories, list for images
  • UDTs: Order items as frozen UDT for clean structure
  • Snapshot data: Store product info in order at purchase time
  • Multiple tables: Different query patterns = different tables
Scenario 3
IoT / Time-series
🌡️ Temperature Monitoring System
Design a data model for an IoT system that collects temperature readings from thousands of sensors every minute. Support real-time monitoring and historical analysis.
Required Queries
  • Q1: Get latest reading from a sensor
  • Q2: Get sensor readings for last 24 hours
  • Q3: Get all readings for specific date range
  • Q4: Get hourly averages for a sensor
  • Q5: Get all sensors in a location
💡 Modeling Considerations
  • 1 reading per minute per sensor = 1.4M reads/day per sensor
  • Keep partitions under 100MB
  • Old data should auto-expire (TTL)
  • Efficient for time-series queries
Data Model Solution

📋 Table 1: Sensor Readings (Time Bucketed)

CREATE TABLE sensor_readings ( sensor_id text, bucket text, -- 'YYYY-MM-DD' for daily bucket reading_time timestamp, temperature decimal, humidity decimal, battery_level int, PRIMARY KEY ((sensor_id, bucket), reading_time) ) WITH CLUSTERING ORDER BY (reading_time DESC) AND compaction = { 'class': 'TimeWindowCompactionStrategy', 'compaction_window_size': '24', 'compaction_window_unit': 'HOURS' } AND default_time_to_live = 2592000; -- 30 days

Why: Time bucketing keeps partitions bounded. TWCS for time-series. TTL auto-expires. Q1-3 ✓

📋 Table 2: Sensor Metadata

CREATE TABLE sensors ( sensor_id text PRIMARY KEY, location text, building text, floor int, room text, install_date timestamp, last_reading timestamp, status text );

Why: Static sensor information. Fast lookups by sensor_id.

📋 Table 3: Hourly Aggregates

CREATE TABLE hourly_stats ( sensor_id text, hour timestamp, -- Truncated to hour avg_temp decimal, min_temp decimal, max_temp decimal, reading_count int, PRIMARY KEY (sensor_id, hour) ) WITH CLUSTERING ORDER BY (hour DESC) AND default_time_to_live = 31536000; -- 1 year

Why: Pre-computed hourly stats for fast analytics. Longer retention than raw data. Q4 ✓

📋 Table 4: Sensors by Location

CREATE TABLE sensors_by_location ( location text, sensor_id text, building text, floor int, room text, PRIMARY KEY (location, sensor_id) );

Why: Find all sensors in a location. Denormalized for performance. Q5 ✓

🎯 Key Design Decisions
  • Time bucketing: Partition by (sensor_id, day) to limit partition size
  • TWCS compaction: Perfect for time-series workloads
  • TTL: Auto-expire raw data after 30 days, aggregates after 1 year
  • Aggregation: Pre-compute hourly stats for fast queries
  • DESC ordering: Latest readings first (most common query)
Scenario 4
Gaming
🎮 Multiplayer Game Leaderboard
Design a data model for a mobile game with millions of players. Support global leaderboards, friend rankings, and player stats with real-time updates.
Required Queries
  • Q1: Get player profile and stats
  • Q2: Get top 100 players globally
  • Q3: Get player's ranking among friends
  • Q4: Get player's match history
  • Q5: Update player score after each game
💡 Modeling Considerations
  • Leaderboard updates must be near real-time
  • Support multiple leaderboard types (daily, weekly, all-time)
  • Millions of concurrent players
  • Fast writes for score updates
Data Model Solution

📋 Table 1: Player Profiles

CREATE TABLE players ( player_id uuid PRIMARY KEY, username text, level int, total_score bigint, games_played int, wins int, losses int, created_at timestamp );

Why: Quick player lookup. Store aggregate stats. Q1 ✓

📋 Table 2: Global Leaderboard

CREATE TABLE leaderboard_global ( board_type text, -- 'daily', 'weekly', 'alltime' score bigint, player_id uuid, username text, updated_at timestamp, PRIMARY KEY (board_type, score, player_id) ) WITH CLUSTERING ORDER BY (score DESC, player_id ASC);

Why: Partition by board type, cluster by score DESC. Top players = LIMIT 100. Q2 ✓

📋 Table 3: Friend Rankings

CREATE TABLE friend_leaderboard ( player_id uuid, score bigint, friend_id uuid, friend_username text, PRIMARY KEY (player_id, score, friend_id) ) WITH CLUSTERING ORDER BY (score DESC);

Why: Each player has their own friend leaderboard. Updated when friends' scores change. Q3 ✓

📋 Table 4: Match History

CREATE TABLE match_history ( player_id uuid, match_id timeuuid, match_date timestamp, opponent_id uuid, opponent_username text, score int, result text, -- 'win', 'loss', 'draw' PRIMARY KEY (player_id, match_id) ) WITH CLUSTERING ORDER BY (match_id DESC);

Why: Recent matches first. Partition per player. Q4 ✓

📋 Score Update Pattern

-- After match completion: BEGIN BATCH -- Update player stats UPDATE players SET total_score = total_score + 100, games_played = games_played + 1, wins = wins + 1 WHERE player_id = ?; -- Update leaderboards (delete old, insert new) DELETE FROM leaderboard_global WHERE board_type = 'alltime' AND score = ? AND player_id = ?; INSERT INTO leaderboard_global (board_type, score, player_id, username) VALUES ('alltime', ?, ?, ?); APPLY BATCH;

Why: Atomic updates across tables. Delete old leaderboard entry, insert new. Q5 ✓

🎯 Key Design Decisions
  • Score clustering: Cluster by score DESC for natural leaderboard ordering
  • Multiple leaderboards: Partition by board_type (daily/weekly/alltime)
  • Delete-insert pattern: Update leaderboard by deleting old, inserting new
  • Denormalization: Store username in leaderboard for display
  • Batching: Atomic updates across player and leaderboard tables
Scenario 5
Messaging
💬 WhatsApp-like Messaging App
Design a data model for a messaging application supporting one-on-one chats, group chats, message delivery status, and conversation history.
Required Queries
  • Q1: Get conversation between two users
  • Q2: Get all conversations for a user
  • Q3: Get unread message count
  • Q4: Get messages in a group chat
  • Q5: Update message delivery status
💡 Modeling Considerations
  • Messages must be in chronological order
  • Support message editing and deletion
  • Real-time delivery updates
  • Efficient pagination for long conversations
Data Model Solution

📋 Table 1: Messages (1-on-1)

CREATE TABLE messages ( conversation_id uuid, -- Deterministic: min(user1,user2)+max(user1,user2) message_id timeuuid, sender_id uuid, content text, status text, -- 'sent', 'delivered', 'read' edited boolean, deleted boolean, PRIMARY KEY (conversation_id, message_id) ) WITH CLUSTERING ORDER BY (message_id DESC);

Why: Conversation = partition, messages time-ordered. Both users query same conversation_id. Q1 ✓

📋 Table 2: User Conversations

CREATE TABLE user_conversations ( user_id uuid, conversation_id uuid, other_user_id uuid, other_username text, last_message text, last_message_time timestamp, unread_count int, PRIMARY KEY (user_id, last_message_time, conversation_id) ) WITH CLUSTERING ORDER BY (last_message_time DESC);

Why: Conversation list per user, ordered by most recent activity. Shows preview. Q2 ✓

📋 Table 3: Unread Messages

CREATE TABLE unread_messages ( user_id uuid, conversation_id uuid, message_id timeuuid, sender_id uuid, content text, received_at timestamp, PRIMARY KEY (user_id, conversation_id, message_id) );

Why: Track unread messages per user. Delete when read. Q3 ✓

📋 Table 4: Group Messages

CREATE TABLE group_messages ( group_id uuid, message_id timeuuid, sender_id uuid, sender_username text, content text, PRIMARY KEY (group_id, message_id) ) WITH CLUSTERING ORDER BY (message_id DESC);

Why: Group chat = single partition. All members read from same partition. Q4 ✓

📋 Delivery Status Update

-- When message is read: BEGIN BATCH -- Update message status UPDATE messages SET status = 'read' WHERE conversation_id = ? AND message_id = ?; -- Remove from unread DELETE FROM unread_messages WHERE user_id = ? AND conversation_id = ? AND message_id = ?; -- Update conversation unread count UPDATE user_conversations SET unread_count = 0 WHERE user_id = ? AND last_message_time = ? AND conversation_id = ?; APPLY BATCH;

Why: Atomic status updates across all relevant tables. Q5 ✓

🎯 Key Design Decisions
  • Deterministic conversation_id: Both users share same partition
  • Denormalization: Conversation list duplicates last message for preview
  • Unread tracking: Separate table for fast unread queries
  • Time ordering: timeuuid for natural message ordering
  • Status updates: Batch updates for consistency
Advertisement

Responsive Ad