Reviewed by 6 specialized AI reviewers. Explore the diagram and the full per-section feedback below.
Loading diagram…
# Ticket Booking Flow ## 1. User Initiates Booking The user selects seat A1 and submits a booking request. ```http POST /bookings { "eventId": "E1", "seatIds": ["A1"] } ``` The client includes an idempotency key so that retries caused by network failures do not create duplicate bookings. --- ## 2. Seat Hold Acquisition The Booking Service first attempts to acquire a temporary hold on the seat using Redis. ```redis SET seat:A1 booking123 NX EX 300 ``` Where: * NX ensures only one user can acquire the hold. * EX 300 sets a 5-minute expiration. If another user attempts to reserve the same seat concurrently, the command fails and the booking request is rejected. --- ## 3. Persist Booking and Seat Hold Redis is not treated as the source of truth. After successfully acquiring the Redis hold, the Booking Service updates PostgreSQL in a transaction. ### Booking Table ```text booking_id user_id event_id status created_at ``` Status: ```text PENDING_PAYMENT CONFIRMED EXPIRED FAILED ``` ### Seat Table ```text seat_id event_id status booking_id hold_expires_at ``` Status: ```text AVAILABLE HELD BOOKED ``` The seat is updated as: ```text Seat A1 status = HELD booking_id = booking123 hold_expires_at = now() + 5 minutes ``` The Booking Service creates: ```text Booking(status=PENDING_PAYMENT) ``` and writes a: ```text BookingCreated ``` event to the Outbox table within the same transaction. This guarantees atomicity between database updates and event publication. --- ## 4. CDC Publishes Booking Event A CDC process reads the Outbox table and publishes: ```text BookingCreated ``` to Kafka. ```text Booking Service ↓ Outbox ↓ CDC ↓ Kafka ``` --- ## 5. Payment Service Creates Payment Record The Payment Service consumes the BookingCreated event. Before calling the payment gateway, it creates a row in its own Payments database. ### Payments Table ```text payment_id booking_id status payment_url gateway_reference created_at ``` Status: ```text CREATING_SESSION SESSION_CREATED PAYMENT_SUCCESS PAYMENT_FAILED REFUNDED ``` The booking_id is unique to prevent duplicate payment sessions during retries. --- ## 6. Create Payment Session The Payment Service creates a payment session with the external payment provider using an idempotency key. ```text Payment Service ↓ Payment Gateway ``` The gateway returns: ```text gateway_reference payment_url ``` The Payment Service updates its database: ```text status = SESSION_CREATED payment_url = ... gateway_reference = ... ``` If the Payment Service crashes after creating the session but before saving the response, retries are protected using the gateway idempotency key and the unique booking_id constraint. --- ## 7. Client Polls Booking Status The Booking Service immediately returns: ```json { "bookingId": "booking123", "status": "PENDING_PAYMENT" } ``` The client periodically polls: ```http GET /bookings/{bookingId} ``` The Booking Service checks payment status through a service API or synchronized payment state and returns: ```json { "status": "PAYMENT_READY", "paymentUrl": "https://gateway.com/pay/xyz" } ``` The client is redirected to the payment gateway. --- ## 8. User Completes Payment The user completes payment on the external payment gateway. --- ## 9. Payment Confirmation The payment gateway sends a webhook. ```text Payment Gateway ↓ Load Balancer ↓ Any Payment Service Instance ``` The webhook is not tied to the instance that created the payment session. Since payment state is persisted in the Payments database, any healthy Payment Service instance can process the webhook. The Payment Service updates: ```text status = PAYMENT_SUCCESS ``` and publishes: ```text PaymentSucceeded ``` to Kafka. Webhook processing is idempotent because gateways may retry webhooks multiple times. --- ## 10. Booking Service Confirms Booking The Booking Service consumes: ```text PaymentSucceeded ``` Before marking the booking successful, it validates ownership of the seat. Expected state: ```text Seat A1 status = HELD booking_id = booking123 ``` Atomic transition: ```text Seat: HELD → BOOKED Booking: PENDING_PAYMENT → CONFIRMED ``` Only the booking that owns the seat hold can perform this transition. --- ## 11. Hold Expiration Handling A background job continuously scans: ```sql status = HELD AND hold_expires_at < NOW() ``` Expired holds are released. ```text Seat: HELD → AVAILABLE Booking: PENDING_PAYMENT → EXPIRED ``` Redis keys naturally expire via TTL. PostgreSQL remains the source of truth. --- ## 12. Redis Failure Recovery If Redis crashes: ```text seat:A1 key disappears ``` Seat ownership is not lost because PostgreSQL contains: ```text status = HELD booking_id = booking123 hold_expires_at ``` Redis can be rebuilt from PostgreSQL by loading all active holds: ```sql status='HELD' AND hold_expires_at > NOW() ``` This prevents inventory corruption due to cache loss. --- ## 13. Late Payment Scenario Consider: ```text 12:00 Seat Held 12:05 Hold Expires 12:05 Seat Released 12:05 Another User Books Seat 12:06 Original Payment Completes ``` The Booking Service validates the seat state before confirming. Expected: ```text status = HELD booking_id = booking123 ``` If validation fails: ```text Seat no longer belongs to booking123 ``` the booking is rejected. The system then initiates a compensation workflow: ```text PaymentSucceeded ↓ BookingConfirmationFailed ↓ RefundRequested ↓ Payment Service ↓ Gateway Refund ``` The user is refunded and double booking is prevented. Virtual Waiting Room for Hot Events: For extremely high-demand events (e.g., IPL Finals, World Cup Finals, major concerts), allowing all users to directly access the booking system can overwhelm downstream services such as Redis, PostgreSQL, and the Booking Service. To prevent this, a Virtual Queue Service is placed in front of the booking flow. When ticket sales open, users are assigned a queue position and stored in a Redis-backed waiting room. An admission controller continuously allows only a limited number of users (e.g., 5,000–10,000 concurrent users) into the booking system based on current capacity. Admitted users receive a short-lived booking token that must be presented when accessing seat maps or creating bookings. This ensures that the booking system processes a controlled amount of traffic, prevents thundering herd problems during flash sales, and significantly improves system stability while maintaining a fair ordering mechanism for users. For the search event we are using the elastic search which is great for location based search as well as text based search. The event data is generally wirte once read many and does not change very often, Thus via CDC we can capture the changes and update the data in elastic search but search can be served via the elastic search.
Draw your architecture for Ticket Booking System and get an instant hire/no-hire signal from 6 specialized AI reviewers — free to start.