MongoDB from Basics to Production · Capstone

Capstone: Design the Data Layer for Event Ticketing

Design MongoDB collections, indexes, consistency and operations for a ticketing platform with flash sales and seat reservations.

Create a free account to save this page, take notes and track your progress. Sign in

Scenario

A ticketing platform sells seats for concerts. Popular events sell out in minutes: tens of thousands of fans try to reserve seats at once. Reservations hold seats for 10 minutes until payment; unpaid holds must be released. Organisers see live sales dashboards, and fans see their tickets and order history.

Requirements

  • No seat is ever sold twice.
  • Held seats are released automatically after 10 minutes.
  • Fans' "my tickets" page is fast; organisers' dashboards update within seconds.
  • The system survives a database node failure without losing paid orders.

Constraints

  • MongoDB replica set (three nodes); Python services; about 500 events per month, up to 80,000 seats per event.

Deliverables

  1. Collections and document design.
  2. Seat reservation without double booking.
  3. Indexes.
  4. Expiring holds and dashboards.
  5. Durability and operations.

Hints

  • Single-document atomic updates are your best friend.
  • Think about which documents get hot during a flash sale.

Draft each deliverable below before revealing the model answer and rubric.

Further reading: Data modeling · Transactions

Deliverables

Describe the collections and document design.

Model answer

  • events (details, venue, sale times), seats (one document per seat: eventId, section, row, number, status available|held|sold, holdId, heldUntil), orders (fan, event, seats copied with price at purchase, payment status), fans.
  • Seats as individual documents (not one array per event) so updates don't contend on one huge document and arrays stay bounded.
  • Orders embed the purchased seats (historical copy) for a fast "my tickets" page.

Rubric

Look for per-seat documents to avoid a hot, unbounded event document, and embedded historical data in orders.

How do you reserve seats without double booking?

Model answer

  • Atomic conditional update per seat: updateOne({_id, status: "available"}, {$set: {status: "held", holdId, heldUntil}}); if modifiedCount is 0, the seat is taken.
  • Multi-seat requests: reserve seat by seat and release the ones already held if any fails (or a short transaction over the seats, with retries on write conflicts).
  • Payment confirmation: updateMany({holdId, status: "held"}, {$set: {status: "sold"}}) plus the order insert in a transaction; idempotency key on the payment webhook.

Rubric

Strong answers use conditional atomic updates (compare-and-set) rather than read-then-write, with a clear plan for multi-seat holds and idempotent payment.

Which indexes do you create?

Model answer

  • seats: {eventId: 1, status: 1, section: 1} for availability maps; {holdId: 1} for confirmations; {status: 1, heldUntil: 1} for releasing expired holds.
  • orders: {fanId: 1, createdAt: -1} for "my tickets"; unique {paymentId: 1} for idempotent webhooks.
  • Verify with explain under realistic data; avoid extra indexes on the hot seats collection.

Rubric

Look for indexes matching each access pattern (ESR order), uniqueness for idempotency, and restraint on write-heavy collections.

How do you expire holds and feed dashboards?

Model answer

  • A worker releases holds every few seconds: updateMany({status: "held", heldUntil: {$lt: now}}, {$set: {status: "available"}, $unset: {holdId: "", heldUntil: ""}}). TTL indexes delete documents, so they don't fit status changes.
  • Dashboards: a change stream on orders and seats updates per-event counters (pre-aggregated documents) and pushes to organisers via WebSockets; store resume tokens.

Rubric

Strong answers notice TTL deletes documents (unsuitable for status resets), use a precise release job, and drive live dashboards from change streams with pre-aggregation.

How do you ensure durability and operate the system?

Model answer

  • w: "majority" for orders and sold seats; retryable writes on; readConcern: "majority" for confirmations.
  • Load-test flash sales; monitor operation latency, write conflicts, replication lag and connection counts; scale reads for event pages with caching.
  • Backups with point-in-time recovery; schema validation on orders and seats; alert when holds aren't released.

Rubric

Look for majority write concern on money paths, load testing of the hot path, observability, and backups with point-in-time recovery.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?