Publisher / Subscriber• Patterns: Observer, State, Mediator
Online Auction System
Medium
Problem Summary
Design an Online Auction platform managing listings, real-time bid validation, bidder price ticker notifications, auction lifecycle state transitions, and auto-sniping time extensions.
Functional Scope
- Auction lifecycle states: Created, Active, Extended, Closed, Settled.
- Bid validation: bid amount must exceed current highest bid + minimum increment.
- Real-time pub-sub broadcasting of incoming bids to all watching bidders.
- Anti-sniping auto-extension: extend auction by 2 minutes if bid placed in final 60 seconds.
- Payment and asset transfer upon auction settlement.
Entity-Relationship (ER) Schema
Auction [1] --> [1] AuctionState Auction [1] --> [*] BidderObserver Auction [1] --> [*] Bid BidderObserver <|.. ClientWebSocket
Design Approach
Apply the Observer pattern for live price updates to all connected bidders. Enforce synchronized locks or Redis Lua scripts during bid submission to prevent race conditions during high-volume auction closings.
Core Classes & Models
Auction (Item listing, reserve price, current high bid)Bid (BidId, bidderId, amount, timestamp)AuctionState (CreatedState, ActiveState, ClosedState)BidderObserver (Broadcasts live price ticker to connected bidders)AuctioneerMediator (Coordinates bids across concurrent participants)
Code Blueprint
96 lines/**
* Low-Level Design: Online Auction System
* Design Patterns Applied:
* - Observer Pattern: Real-time outbid and auction status notifications to watchers
* - State Pattern: Auction listing lifecycle (Draft, Active, Closed, Settled)
* - Mediator Pattern: Auctioneer coordinating bids between buyers and sellers
*/
import java.util.*;
enum AuctionState { UPCOMING, ACTIVE, CLOSED }
// --- [DESIGN PATTERN: Observer] Observer for auction watchers ---
interface BidderObserver {
void onNewHighestBid(String itemName, double amount, String bidderId);
void onAuctionClosed(String itemName, String winnerId, double finalAmount);
}
class Bidder implements BidderObserver {
private final String id, name;
public Bidder(String id, String name) { this.id = id; this.name = name; }
public String getId() { return id; }
public String getName() { return name; }
@Override
public void onNewHighestBid(String itemName, double amount, String bidderId) {
if (!bidderId.equals(this.id)) {
System.out.printf("📢 [Alert to %s] Outbid on '%s'! New top bid: $%.2f (by %s)%n", name, itemName, amount, bidderId);
}
}
@Override
public void onAuctionClosed(String itemName, String winnerId, double finalAmount) {
if (winnerId.equals(this.id)) {
System.out.printf("🏆 [Alert to %s] CONGRATULATIONS! You won '%s' for $%.2f!%n", name, itemName, finalAmount);
} else {
System.out.printf("🏁 [Alert to %s] Auction for '%s' closed. Winner: %s ($%.2f)%n", name, itemName, winnerId, finalAmount);
}
}
}
// --- [DESIGN PATTERN: Mediator] Auctioneer mediating between bidders ---
class AuctionItem {
private final String id, title;
private double currentBid;
private String highestBidderId;
private AuctionState state = AuctionState.UPCOMING;
private final List<BidderObserver> watchers = new ArrayList<>();
public AuctionItem(String id, String title, double startingPrice) {
this.id = id; this.title = title; this.currentBid = startingPrice;
}
public void addWatcher(BidderObserver watcher) { watchers.add(watcher); }
public void startAuction() {
this.state = AuctionState.ACTIVE;
System.out.println("🟢 Auction started for item: " + title + " (Reserve: $" + currentBid + ")");
}
public synchronized boolean placeBid(String bidderId, double amount) {
if (state != AuctionState.ACTIVE) {
System.out.println("❌ Cannot bid: Auction is not active!");
return false;
}
if (amount <= currentBid) {
System.out.println("❌ Bid $" + amount + " is lower than current highest bid $" + currentBid);
return false;
}
this.currentBid = amount;
this.highestBidderId = bidderId;
for (BidderObserver b : watchers) b.onNewHighestBid(title, amount, bidderId);
return true;
}
public synchronized void closeAuction() {
this.state = AuctionState.CLOSED;
for (BidderObserver b : watchers) b.onAuctionClosed(title, highestBidderId, currentBid);
}
}
public class Main {
public static void main(String[] args) {
AuctionItem item = new AuctionItem("AUC-101", "Vintage Rolex Submariner", 5000.0);
Bidder alice = new Bidder("B1", "Alice");
Bidder bob = new Bidder("B2", "Bob");
item.addWatcher(alice);
item.addWatcher(bob);
item.startAuction();
item.placeBid(alice.getId(), 5500.0);
item.placeBid(bob.getId(), 6200.0);
item.closeAuction();
}
}