Architecture
The Booking Kit is organized into three layers that can be used independently or together.
System layers
Section titled “System layers”┌─────────────────────────────────────────────────────┐│ Your Next.js App │├──────────┬──────────────────┬────────────────────────┤│ @thebookingkit/ui │ API Routes ││ 21 React components │ (your code) ││ Copy-paste, you own them │ │├──────────┴──────────────────┤ ││ @thebookingkit/core │ ││ Slot engine, scheduling │ ││ math, business logic │ │├─────────────────────────────┼────────────────────────┤│ @thebookingkit/db │ Adapter Interfaces ││ Drizzle schema, 23 tables │ Auth · Email · Jobs ││ PostgreSQL 15+ │ Calendar · Payment │└─────────────────────────────┴────────────────────────┘1. Logic layer (@thebookingkit/core)
Section titled “1. Logic layer (@thebookingkit/core)”Pure TypeScript with zero framework dependencies. Contains:
- Slot engine — Three-step computation pipeline (RRULE → overrides → bookings)
- Timezone utilities — UTC normalization and local display
- RRULE parser — iCalendar recurrence expansion with EXDATE support
- Booking limits — Daily/weekly/monthly caps
- Team scheduling — Round-robin, collective, managed, fixed strategies
- Payments — Cancellation policies, fee computation
- Workflows — Trigger-condition-action evaluation
- Webhooks — HMAC signing, verification, retry logic
- Auth middleware — Provider ownership and customer access assertions
- Adapter interfaces — TypeScript interfaces for external services
2. Data layer (@thebookingkit/db)
Section titled “2. Data layer (@thebookingkit/db)”Drizzle ORM schema with 23 PostgreSQL tables:
- Full TypeScript type inference from schema definitions
- Custom SQL migrations for
btree_gist, audit triggers, and GDPR anonymization EXCLUDE USING gistconstraint for database-level double-booking preventionSERIALIZABLEtransactions with automatic retry (withSerializableRetry())
3. UI layer (@thebookingkit/ui)
Section titled “3. UI layer (@thebookingkit/ui)”21 React components following the shadcn/ui convention:
- Copy source code into your project — you own it completely
- Built on react-day-picker, react-big-calendar, react-hook-form
- All components accept
classNameandstyleprops for customization
Slot computation pipeline
Section titled “Slot computation pipeline”The core algorithm follows a three-step model:
RRULE rules → Base windows → Apply overrides → Subtract bookings → Available slots (expand) (mask) (filter + buffer)- Base Layer: Expand
availability_rulesvia RRULE into time windows for the requested date range - Mask Layer: Apply
availability_overrides— block dates, shorten hours, or add extra hours - Filter Layer: Subtract existing bookings and their buffer time to produce available slots
Adapter pattern
Section titled “Adapter pattern”External dependencies are abstracted behind TypeScript interfaces. Swap implementations without changing business logic:
| Adapter | Purpose | Default | Alternatives |
|---|---|---|---|
AuthAdapter | Authentication | NextAuth.js | Supabase Auth, Clerk, Lucia |
EmailAdapter | Transactional email | Resend | SendGrid, AWS SES, Postmark |
CalendarAdapter | Calendar sync | Google Calendar | Outlook, CalDAV |
JobAdapter | Background jobs | Inngest | Trigger.dev, BullMQ |
PaymentAdapter | Payment processing | Stripe | — |
StorageAdapter | Secret storage | Env vars | Vault, KMS |
SmsAdapter | SMS notifications | — | Twilio, MessageBird |
// Example: implement the EmailAdapter interfaceconst myEmailAdapter: EmailAdapter = { async sendEmail(options: SendEmailOptions): Promise<EmailResult> { // Your implementation return { messageId: "abc", status: "sent" }; },};
// Pass it to notification functionsawait sendConfirmationEmail(payload, myEmailAdapter);Double-booking prevention
Section titled “Double-booking prevention”The Booking Kit prevents double-bookings at two levels:
- Database constraint:
EXCLUDE USING gistwithbtree_gistextension ensures no two confirmed bookings overlap for the same provider - Application-level:
SERIALIZABLEtransactions withwithSerializableRetry()— catches serialization failures (SQLSTATE 40001) and retries up to 3 times with jittered exponential backoff
This means double-bookings are impossible even under high concurrent load.