Back to LLD explorer
Publisher / Subscriber• Patterns: Observer, Strategy, Factory

Cricbuzz

Medium

Problem Summary

Design a high-scale Live Cricket Commentary and Scoreboard engine broadcasting ball-by-ball events, player statistics updates, and Duckworth-Lewis-Stern target projections in real time.

Functional Scope

  • Ball-by-ball event processing: runs, wickets, extras (wides, no-balls), reviews.
  • Real-time pub-sub broadcasting to millions of fan clients via WebSockets and push notifications.
  • Dynamic target projection strategies (Run Rate extrapolation, DLS method).
  • Per-player batting and bowling live statistics accumulation.
  • Auditing and compensation support for DRS umpire reversals.

Entity-Relationship (ER) Schema

Match [1] --> [2] Innings
Innings [1] --> [*] Over
Over [1] --> [6..*] BallEvent
Match [1] --> [*] ScoreboardObserver
Match [1] --> [1] TargetCalculatorStrategy

Design Approach

Implement the Observer pattern to decouple ball-by-ball state mutations from downstream delivery mechanisms (WebSockets, mobile push, and search indexing). Protect match score mutations with thread-safe collections or actor queues.

Core Classes & Models

Match (Context managing teams, current innings, and observer registry)Innings (Tracks runs, wickets, overs, and batting/bowling lineups)BallEvent (Type, batsman, bowler, runs, wicket details)ScoreboardObserver (WebSocket broadcaster, Push notification dispatcher, Analytics logger)TargetCalculatorStrategy (DLSTargetStrategy, SimpleRunRateStrategy)
Code Blueprint
75 lines
/**
 * Low-Level Design: Cricbuzz Live Cricket Scoreboard
 * Design Patterns Applied:
 *  - Observer Pattern: Real-time ball-by-ball score and wicket broadcasts to thousands of subscribers
 *  - Strategy Pattern: Dynamic Net Run Rate (NRR) and strike-rate calculation strategies
 *  - Factory Pattern: Match, Innings, and Over object generation
 */

import java.util.*;

// --- [DESIGN PATTERN: Observer] Observer for live score updates ---
interface ScoreSubscriber {
    void onScoreUpdated(int runs, int wickets, double overs, String commentary);
}

class MobileAppSubscriber implements ScoreSubscriber {
    private final String userId;
    public MobileAppSubscriber(String userId) { this.userId = userId; }
    @Override
    public void onScoreUpdated(int runs, int wickets, double overs, String commentary) {
        System.out.printf("📱 [App User: %s] %d/%d (%.1f ov) -> %s%n", userId, runs, wickets, overs, commentary);
    }
}

// --- [DESIGN PATTERN: Strategy] Run Rate calculation algorithm ---
interface RunRateStrategy {
    double calculate(int totalRuns, int ballsBowled);
}

class CurrentRunRateStrategy implements RunRateStrategy {
    @Override
    public double calculate(int totalRuns, int ballsBowled) {
        if (ballsBowled == 0) return 0.0;
        return (totalRuns * 6.0) / ballsBowled;
    }
}

// --- [DESIGN PATTERN: Subject / Match Coordinator] ---
class MatchScoreboard {
    private int runs = 0;
    private int wickets = 0;
    private int totalBalls = 0;
    private final List<ScoreSubscriber> subscribers = new ArrayList<>();
    private final RunRateStrategy crrStrategy = new CurrentRunRateStrategy();

    public void subscribe(ScoreSubscriber sub) { subscribers.add(sub); }
    public void unsubscribe(ScoreSubscriber sub) { subscribers.remove(sub); }

    public synchronized void recordBall(int runsScored, boolean isWicket, String commentary) {
        this.runs += runsScored;
        if (isWicket) this.wickets++;
        this.totalBalls++;

        double overs = (totalBalls / 6) + (totalBalls % 6) / 10.0;
        double crr = crrStrategy.calculate(runs, totalBalls);
        String enrichedCommentary = commentary + String.format(" [CRR: %.2f]", crr);

        for (ScoreSubscriber sub : subscribers) {
            sub.onScoreUpdated(runs, wickets, overs, enrichedCommentary);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MatchScoreboard scoreboard = new MatchScoreboard();
        scoreboard.subscribe(new MobileAppSubscriber("User_101"));
        scoreboard.subscribe(new MobileAppSubscriber("User_202"));

        System.out.println("--- Starting Live Match Simulation ---");
        scoreboard.recordBall(4, false, "FOUR! Driven through extra cover.");
        scoreboard.recordBall(0, true, "OUT! Clean bowled through the gate!");
        scoreboard.recordBall(6, false, "SIX! Massive strike over long-on!");
    }
}