Design Cricbuzz

08 Aug 2026, Updated: 10 Aug 2026 13 min read
1
A Cricbuzz-like cricket platform provides live scores, ball-by-ball commentary, match information, scorecards, player statistics, and complete match commentary.

The most important part of the system is delivering the latest commentary with very low latency while allowing users to browse the full commentary of an ongoing or completed match.

The system should support millions of concurrent users during popular cricket matches, where a single ball can generate a large burst of reads from users around the world.

The platform should also maintain the complete commentary history so that users joining late can immediately retrieve previous overs and users can browse the entire match after it has completed.

Requirements

Functional Requirements

The Cricbuzz-like system should satisfy the following functional requirements.

1. Display live matches.
2. Display the latest ball-by-ball commentary.
3. Display full match commentary.
4. Display live score and scorecard.
5. Allow users to navigate between overs and innings.
6. Display batsman and bowler information.
7. Display match and player statistics.
8. Send notifications for important match events.
9. Support completed matches and historical commentary.

Non-Functional Requirements

The system should also satisfy the following non-functional requirements.

1. Very low latency for latest commentary.
2. High availability.
3. Horizontal scalability.
4. High read throughput.
5. Fault tolerance.
6. Strong ordering of commentary events.
7. Support millions of concurrent users during major matches.

API Design

The platform exposes APIs for live match information, latest commentary, full commentary, and scorecards.

Get Live Match

GET /api/v1/matches/M101
{
  "matchId": "M101",
  "status": "LIVE",
  "score": "182/4",
  "overs": "32.4",
  "battingTeam": "India",
  "bowlingTeam": "Australia"
}

Get Latest Commentary

GET /api/v1/matches/M101/commentary/latest
{
  "matchId": "M101",
  "innings": 2,
  "over": 32,
  "ball": 5,
  "score": "182/4",
  "commentary": "FOUR! Driven beautifully through the covers."
}

Get Full Commentary

GET /api/v1/matches/M101/commentary?innings=2&cursor=cursor_123
{
  "matchId": "M101",
  "innings": 2,
  "commentary": [
    {
      "over": 32,
      "ball": 5,
      "text": "FOUR! Driven beautifully through the covers."
    },
    {
      "over": 32,
      "ball": 4,
      "text": "Single taken towards mid-on."
    }
  ],
  "nextCursor": "cursor_456"
}

Get Scorecard

GET /api/v1/matches/M101/scorecard
{
  "matchId": "M101",
  "innings": [
    {
      "team": "India",
      "runs": 182,
      "wickets": 4,
      "overs": "32.4"
    },
    {
      "team": "Australia",
      "runs": 0,
      "wickets": 0,
      "overs": "0.0"
    }
  ]
}
For live updates, the platform can additionally use WebSocket or Server-Sent Events (SSE) so clients receive score and commentary updates without repeatedly polling the APIs.

High-Level Architecture

The following diagram illustrates the high-level architecture of a Cricbuzz-like live cricket commentary system.

The system consists of services such as the Match Service, Commentary Service, Score Service, Statistics Service, Notification Service, and API Gateway.

The most important component is the Commentary Service, which receives ball-by-ball updates and makes the latest commentary available with very low latency.

API Gateway

The API Gateway acts as the entry point for mobile and web clients.

It authenticates requests where required, applies rate limiting, and routes requests to the appropriate service.

Requests for the latest commentary are routed to the Commentary Service, while scorecard requests are routed to the Score Service.

The API Gateway should also support efficient caching headers and compression because the same match data may be requested by millions of clients.

Match Service

The Match Service manages match metadata such as teams, venue, tournament, scheduled start time, innings, and match status. A simplified match table can look like:
MATCH
--------------------------------------------------------------------------------
match_id | tournament | team_a      | team_b      | venue       | status
--------------------------------------------------------------------------------
M101     | IPL 2026    | India      | Australia   | Mumbai      | LIVE
M102     | Test Series | England    | South Africa| London      | COMPLETED
M103     | ODI Series  | Pakistan   | Sri Lanka   | Colombo     | UPCOMING
The Match Service provides the basic information required by clients before they request commentary or scorecard data.

Commentary Service

The Commentary Service manages the complete ball-by-ball commentary lifecycle. It consumes delivery events from Kafka.

The Event Ingestion Service publishes these events after receiving them from third-party scoring APIs or editorial UIs.

The Commentary Service validates and orders the events, stores the commentary history, maintains the latest match state in Redis, and serves both live and historical commentary.

Each delivery event contains the match, innings, delivery sequence, players, runs, extras, wicket information, score, and commentary text. For example:
{
  "eventId": "B635ED1F",
  "matchId": "M101",
  "innings": 2,
  "over": 32,
  "ball": 5,
  "deliverySequence": 325,
  "batsman": "B101",
  "bowler": "BW201",
  "runs": 4,
  "extras": 0,
  "wicket": false,
  "score": "182/4",
  "commentary": "FOUR! Driven beautifully through the covers.",
  "eventTime": "2026-08-10T18:32:15Z"
}
The deliverySequence provides ordering within the match, while the unique eventId allows the Commentary Service to detect duplicate events during retries or reprocessing.

Commentary Data Model

The complete commentary history is stored in a durable relational database such as PostgreSQL.
COMMENTARY
----------------------------------------------------------------------------------------------------------------
commentary_id    | match_id | innings | over | ball_seq | batsman | bowler | runs | wicket | commentary_text
----------------------------------------------------------------------------------------------------------------
2993D99C         | M101     | 2       | 32   | 5        | B101    | BW201  | 4    | false  | FOUR! Driven through covers.
E8231A86         | M101     | 2       | 32   | 4        | B101    | BW201  | 1    | false  | Single taken towards mid-on.
65AFFBAF         | M101     | 2       | 32   | 3        | B101    | BW201  | 0    | false  | Defended back to the bowler.
The commentary_id uniquely identifies each event. The combination of match_id, innings, and ball_seq identifies its position within the innings.

A separate ball_seq is useful because wides and no-balls generate delivery events without necessarily advancing the legal-ball count.

The model can therefore preserve the exact event sequence while still maintaining the cricket over and ball information.

Commentary Ingestion

Kafka provides a durable event stream and allows multiple consumers, such as the Commentary, Score, Statistics, and Notification services, to process the same match events independently.

For ordering, events for the same match should use the same Kafka partition key, such as match_id. Kafka then preserves their partition order.

When the Commentary Service receives a delivery event, it first validates the event and checks its sequence. It then persists the event in PostgreSQL and updates the latest match state in Redis.

The database should enforce uniqueness on the event or delivery identifier so that Kafka retries do not create duplicate commentary records.
CREATE UNIQUE INDEX uq_commentary_event
ON COMMENTARY(commentary_id);

CREATE INDEX idx_commentary_match
ON COMMENTARY(match_id, innings, ball_seq);
The consumer should process events idempotently because Kafka consumers can receive the same event again after retries or failures.

Latest Commentary

During a live match, millions of users may request the latest score and commentary. Reading PostgreSQL for every request would create unnecessary database load.

The Commentary Service therefore maintains the six most recent deliveries in Redis. The deliveries are stored in descending order, with the latest delivery first.
Key: match:M101:latest

{
  "matchId": "M101",
  "innings": 2,
  "score": "182/4",
  "commentary": [
    {
      "deliverySequence": 325,
      "over": 32,
      "ball": 5,
      "text": "FOUR! Driven beautifully through the covers."
    },
    {
      "deliverySequence": 324,
      "over": 32,
      "ball": 4,
      "text": "Single taken towards mid-on."
    },
    .
    .
    .
    {
      "deliverySequence": 320,
      "over": 31,
      "ball": 6,
      "text": "Single taken."
    }
  ]
}
The latest commentary API reads this data directly from Redis:
GET /api/v1/matches/M101/commentary/latest
When a new delivery is processed, the Commentary Service adds it to the beginning of the Redis list and removes the oldest delivery, keeping only the latest six.

The Commentary Service also sends the new delivery through the WebSocket.

Before updating its local state, the client verifies that the incoming deliverySequence directly follows the latest delivery it already has.

For example, if the client currently has sequence 325 and receives 326, the sequences are continuous, so it prepends the new delivery and removes the oldest entry.

If the client has sequence 325 but receives sequence 328, one or more events were missed. The client does not prepend the event because its local state is no longer synchronized.

The synchronization request is served directly from Redis, so the client can quickly recover the latest six deliveries without querying PostgreSQL.

This mechanism allows the client to detect missed events and recover automatically while keeping the normal WebSocket path lightweight.

Under normal conditions, each new delivery is simply prepended to the existing six-delivery window.

Full Commentary

The complete commentary history remains in PostgreSQL. When users request commentary, the Commentary Service retrieves only the required portion instead of loading the entire match history.

Most cricket websites display commentary in ascending order, starting with the first delivery at the top and the latest delivery at the bottom.

The API can therefore use simple pagination based on deliverySequence. On the first request, the client does not provide a fromSequence, so the API starts from the beginning of the commentary.
GET /api/v1/matches/M101/commentary?innings=2&limit=20
The Commentary Service queries the indexed delivery sequence:
SELECT
    match_id,
    innings,
    over,
    ball,
    delivery_sequence,
    batsman,
    bowler,
    runs,
    wicket,
    commentary_text
FROM COMMENTARY
WHERE match_id = 'M101'
  AND innings = 2
ORDER BY delivery_sequence ASC
LIMIT 20;
The first response contains deliveries 0 through 19:
{
  "matchId": "M101",
  "innings": 2,
  "commentary": [
    {
      "deliverySequence": 0,
      "over": 1,
      "ball": 1,
      "text": "Single taken towards mid-on."
    },
    {
      "deliverySequence": 1,
      "over": 1,
      "ball": 2,
      "text": "Defended back to the bowler."
    },
    .
    .
    .
    {
      "deliverySequence": 19,
      "over": 4,
      "ball": 1,
      "text": "Defended back to the bowler."
    }
  ],
  "lastDeliverySequence": 19
}
The client uses the last received sequence to request the next page:
GET /api/v1/matches/M101/commentary?innings=2&fromSequence=20&limit=20
The query then starts from that sequence:
SELECT
    match_id,
    innings,
    over,
    ball,
    delivery_sequence,
    batsman,
    bowler,
    runs,
    wicket,
    commentary_text
FROM COMMENTARY
WHERE match_id = 'M101'
  AND innings = 2
  AND delivery_sequence >= 20
ORDER BY delivery_sequence ASC
LIMIT 20;
This provides simple, predictable pagination without introducing a separate cursor abstraction.

The same deliverySequence is also used by the WebSocket connection to determine which events the client has already received.

Live Score Updates

The Score Service consumes the same delivery events from Kafka that are consumed by the Commentary Service.

Each delivery event contains the runs, extras, wicket information, innings, and current score required to update the match state.
{
  "eventId": "B635ED1F",
  "matchId": "M101",
  "innings": 2,
  "over": 32,
  "ball": 5,
  "deliverySequence": 325,
  "batsman": "B101",
  "bowler": "BW201",
  "runs": 4,
  "extras": 0,
  "wicket": false,
  "score": "182/4",
  "commentary": "FOUR! Driven beautifully through the covers.",
  "eventTime": "2026-08-10T18:32:15Z"
}
The Score Service processes the delivery event and updates the current match state in both PostgreSQL and Redis.

PostgreSQL provides durable storage, while Redis provides low-latency access to the latest score. A simplified score table can look like:
MATCH_SCORE
--------------------------------------------------------------------------------
match_id | innings | runs | wickets | overs | batting_team | bowling_team
--------------------------------------------------------------------------------
M101     | 2       | 182  | 4       | 32.5  | India        | Australia
The latest score is also maintained in Redis:
Key: match:M101:score

{
  "matchId": "M101",
  "innings": 2,
  "score": "182/4",
  "overs": "32.5",
  "battingTeam": "India",
  "bowlingTeam": "Australia"
}
The Score Service updates Redis whenever a new delivery event is processed.

Client requests for the current score can therefore be served directly from Redis without repeatedly querying PostgreSQL.

Connected clients receive score updates through WebSocket or SSE, allowing the browser or mobile application to update the live score without continuously polling the API.

Scorecard

The same delivery events are also used to build the complete scorecard.

The Score Service processes each delivery independently and incrementally updates the relevant batting, bowling, extras, wicket, partnership, and innings records in PostgreSQL.

For example, when delivery sequence 325 records four runs by batsman B101, the Score Service updates the batsman's runs, the bowler's conceded runs, and the innings total.
UPDATE BATTING_SCORE
SET runs = runs + 4,
    balls_faced = balls_faced + 1
WHERE match_id = 'M101'
  AND innings = 2
  AND player_id = 'B101';
UPDATE BOWLING_SCORE
SET runs_conceded = runs_conceded + 4,
    legal_balls = legal_balls + 1
WHERE match_id = 'M101'
  AND innings = 2
  AND player_id = 'BW201';
UPDATE INNINGS_SCORE
SET runs = runs + 4,
    legal_balls = legal_balls + 1
WHERE match_id = 'M101'
  AND innings = 2;
If the delivery contains extras, a wicket, or other events, the Score Service updates the corresponding records as part of processing the same delivery event.

A simplified scorecard schema can therefore contain separate records for the innings, batting, bowling, and extras:
INNINGS_SCORE
--------------------------------------------------------------------------------
match_id | innings | batting_team | runs | wickets | legal_balls
--------------------------------------------------------------------------------
M101     | 2       | India        | 182  | 4       | 197
BATTING_SCORE
--------------------------------------------------------------------------------
match_id | innings | player_id | runs | balls_faced | fours | sixes | status
--------------------------------------------------------------------------------
M101     | 2       | B101      | 85   | 72          | 8     | 2     | OUT
M101     | 2       | B102      | 54   | 61          | 5     | 1     | NOT_OUT
BOWLING_SCORE
--------------------------------------------------------------------------------
match_id | innings | player_id | overs | runs_conceded | wickets
--------------------------------------------------------------------------------
M101     | 2       | BW201     | 8.5   | 42            | 2
EXTRAS_SCORE
---------------------------------------------------------------
match_id | innings | wides | no_balls | byes | leg_byes | total
---------------------------------------------------------------
M101     | 2       | 4     | 1        | 2    | 3         | 10
Thus, after every delivery, the scorecard gradually evolves from the individual delivery events.

The Score Service does not need to recalculate the entire match from the beginning; it applies the changes represented by each new event.

The durable scorecard state is stored in PostgreSQL, while frequently accessed active-match scorecard data can be cached in Redis.

If required, the scorecard can also be reconstructed from the delivery events because the events are retained in Kafka and persisted as part of the match history.

The scorecard API simply reads the current aggregated state:
GET /api/v1/matches/M101/scorecard

Handling Aggregated Score Events

Some third-party scoring systems may provide the updated score or scorecard state directly as part of a match event, instead of requiring the Score Service to calculate every field from individual delivery events.

In that case, the Score Service can persist the received score information in PostgreSQL and update the corresponding latest state in Redis.

For example, an event may contain:
{
  "eventId": "E326",
  "matchId": "M101",
  "innings": 2,
  "deliverySequence": 326,
  "score": "183/4",
  "overs": "32.6",
  "scorecard": {
    "batting": "...",
    "bowling": "...",
    "extras": "..."
  }
}

Conclusion

The design uses an event-driven architecture where a single delivery event from Kafka can independently update commentary, live score, and scorecard state.

PostgreSQL provides durable storage, while Redis serves frequently accessed live data with low latency. WebSocket/SSE delivers real-time updates to clients without continuous polling.

The use of deliverySequence keeps commentary ordered and allows clients to detect missed updates and resynchronize.

This design separates durable history from hot live state while allowing the system to scale horizontally for large numbers of concurrent viewers.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer β€’ Java β€’ Python β€’ Distributed Systems β€’ AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

πŸ’¬ Comments

Join the Discussion