DEV Community

Saras Growth Space
Saras Growth Space

Posted on

LLD Domain Modeling: Designing BookMyShow (Seat Consistency, Booking Flow & Aggregates)

One of the best systems for learning Low-Level Design is BookMyShow.

Because at first glance, it looks simple:

User selects movie
→ chooses seats
→ makes payment
→ booking gets confirmed
Enter fullscreen mode Exit fullscreen mode

But internally, this system forces us to solve some very important design problems:

  • seat consistency
  • concurrent booking
  • lifecycle management
  • state transitions
  • ownership boundaries
  • workflow orchestration

Which makes it an excellent domain modeling exercise.


Step 1 — Understand the Core Business Flow

Before thinking about classes, understand the workflow first.

Simplified booking flow:

User selects show
→ selects seats
→ seats get locked
→ payment happens
→ booking gets confirmed
Enter fullscreen mode Exit fullscreen mode

Now ask carefully:

Which objects own these responsibilities?

That question drives the design.


Step 2 — Identify Core Entities

We start with business objects that:

  • have identity
  • evolve over time
  • require tracking

Movie

  • movieId
  • title
  • duration
  • language

Movie has business identity.

Entity ✅


Theater

Tracks:

  • location
  • screens
  • configuration

Entity ✅


Show

Very important Entity.

Why?

Because Show controls:

  • timing
  • seat availability
  • booking state
  • pricing context

Show becomes central to consistency.


Booking

Booking has:

  • bookingId
  • lifecycle
  • payment relationship
  • cancellation state

Entity ✅


Step 3 — Identify Value Objects

Now separate descriptive values.


SeatNumber

SeatNumber("A12")
Enter fullscreen mode Exit fullscreen mode

We care about:

  • the value itself not
  • independent lifecycle

Value Object ✅


Money

Money(450)
Enter fullscreen mode Exit fullscreen mode

Again:

  • defined by value
  • no identity

Value Object ✅


TimeRange

Represents:

  • start time
  • end time

Also a Value Object.


Step 4 — Identify Critical Invariants

This is where real system design begins.

Ask:

“What business rules must NEVER break?”


Most Important Invariant

Same seat cannot be booked twice for the same show.
Enter fullscreen mode Exit fullscreen mode

This single rule shapes most of the system design.

Other invariants:

  • cancelled bookings should release seats
  • payment must happen before confirmation
  • locked seats should expire eventually
  • invalid seat transitions must not occur

Step 5 — Find the Correct Aggregate Boundary

This is where many beginner designs fail.

A common mistake is making Booking responsible for seats.

But think carefully.

Who actually owns seat consistency?

The answer is:

Show.


Show Aggregate

Show Aggregate
 ├── Show (Root)
 ├── SeatAvailability
 ├── Pricing
 └── Timing
Enter fullscreen mode Exit fullscreen mode

The Show Aggregate becomes responsible for:

  • locking seats
  • confirming seats
  • releasing seats
  • protecting booking consistency

This is extremely important.


Why Booking Should NOT Control Seats

Imagine this design:

Booking → directly marks seats booked
Enter fullscreen mode Exit fullscreen mode

Now multiple bookings can race against each other.

Consistency becomes fragile.

Instead:

Booking asks Show to reserve seats
Enter fullscreen mode Exit fullscreen mode

Now consistency remains centralized.

That is good aggregate design.


Step 6 — Model Seat State Transitions

Seats evolve through states.

Example:

AVAILABLE
→ LOCKED
→ BOOKED
Enter fullscreen mode Exit fullscreen mode

Or:

LOCKED
→ AVAILABLE
Enter fullscreen mode Exit fullscreen mode

if payment fails or timeout occurs.

These transitions must remain controlled.


Example Domain Logic

class Show {

    void lockSeats(List<SeatNumber> seats) {

        if(!areAvailable(seats)) {
            throw new SeatUnavailableException();
        }

        markLocked(seats);
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice:

  • Show protects consistency
  • invalid transitions are blocked
  • business rules stay centralized

Step 7 — Separate Workflow From Domain Logic

Now comes orchestration.

Booking flow involves:

  • seat locking
  • payment
  • notifications
  • booking confirmation

This coordination belongs in Services.


BookingService

class BookingService {

    void createBooking(User user, Show show, Seats seats) {

        show.lockSeats(seats);

        paymentService.process();

        booking.confirm();

        notificationService.send();
    }
}
Enter fullscreen mode Exit fullscreen mode

Service coordinates workflow.

Show protects consistency.

That separation is critical.


Step 8 — Think Beyond the Happy Path

Strong LLD always considers failures.

Example:

  • payment fails
  • user abandons flow
  • seat lock expires
  • duplicate requests happen

Now state management becomes even more important.

Weak systems fail under these situations.

Strong systems protect consistency despite failures.


Final Domain Model

Entities
- Movie
- Theater
- Show
- Booking

Value Objects
- SeatNumber
- Money
- TimeRange

Aggregate
- Show Aggregate

Invariants
- no double booking
- valid seat lifecycle

Services
- BookingService
- PaymentService
- NotificationService
Enter fullscreen mode Exit fullscreen mode

The Most Important Insight

BookMyShow is not fundamentally a “movie booking system”.

At its core, it is actually:

a consistency management system.

The hardest problem is not:

  • rendering UI
  • exposing APIs
  • storing records

The hardest problem is:

  • protecting valid seat state under concurrent usage.

And that is exactly why domain modeling matters in real Low-Level Design.

Because strong systems are built by identifying:

  • ownership
  • consistency boundaries
  • lifecycle control
  • responsibility separation

before writing large amounts of code.

Top comments (0)