---
title: "Building Scalable Booking Platforms"
canonical: https://zarki.tech/insights/building-scalable-booking-platforms
---

# Building Scalable Booking Platforms

Bookings look like a calendar widget. They are actually inventory, payments, and dispute state. How we model that for dealerships, trainers, and anything with a scarce slot.

By Antoine Khoury Abboud. Published 2026-09-01. Updated 2026-09-01.

A booking platform is not a calendar. A calendar is the view. The product is **who is allowed to take a scarce thing, when, and what happens when they do not show up**.

We have built this shape more than once: [Carovenue](/#work) (listings, bookings, showroom workflows), [Flexa Fitness](/#work) (sessions a trainer actually runs), and internal tools where the “slot” is a crew, a bay, or a site visit.

The UI can be pretty. The domain has to be strict.

## The objects that matter

If your schema is `Appointment { start, end, userId }`, you will rewrite it.

We split:

- **Resource** — the scarce thing (person, bay, vehicle, room)
- **Offering** — what the guest thinks they bought
- **Hold** — a short-lived reservation
- **Booking** — a hold that survived payment / policy
- **Fulfillment** — showed up, finished, cancelled, no-show

Holds exist because two people will tap the same Saturday morning. Without a hold that expires, you get double books and refund theatre.

## Time is a political timezone

Store UTC. Display the location’s zone. Never store “Tuesday 3pm” as a naive string if the business has more than one site.

Construction and dealership work made this non-negotiable: a booking that moves a crew across cities is wrong in a way a gym class in one neighbourhood is not. Design for the harder case if you might grow into it.

## Payments are part of the state machine

Do not bolt Stripe on as “and then we charge.” Map:

- hold without payment
- deposit
- capture
- refund
- no-show fee

to explicit booking states. Support should be able to see the state without opening the payment dashboard. If they cannot, your tools are lying to two systems at once.

## Scale is contention, not traffic

Most booking products do not die at ten million users. They die at **one popular resource** and a burst of taps.

So we:

- serialise holds per resource
- keep the hot path small
- make listing reads cheap and writes boring
- avoid “select for update the whole week” because someone opened a modal

This is the same instinct as [event-sourced construction platforms](/insights/production-ready-nestjs-backends): contention belongs in a narrow gate.

## What the operator needs

Guests see a grid. Operators need:

- overbooking policy that is a setting, not a Slack message
- a way to move a booking without destroying the audit
- a no-show that does not look like a cancel

If you only design the guest flow, you have built a widget.

For a vision-heavy sibling of this problem, see [AI vehicle inspection](/insights/building-ai-vehicle-inspection-system). To scope one, [get a build plan](/#contact).
