Inkress ADRsarchitecture decisions
Overview / Bookings
ADR 0044

Bookings are a thin vertical over the commerce catalog, not a standalone scheduler

AcceptedBookings

Context

Inkress serves not only retail merchants but service businesses that take appointments. The choice was whether to build a standalone scheduling product (its own domain: services, clients, locations, staff availability, waitlists, blocked slots, a public booking site) or to reuse the existing commerce core (catalog, checkout, payments, tenancy) for bookings.

Decision

Model bookings as a thin vertical over the commerce catalog: a booking is a time-boxed reservation of a product variant, not a new standalone entity.

  • A service is a product variant. Booking belongs_to :variant (a required product variant), so a bookable service is just a catalog item with a time box; staff are attached via BookingAttendant and changes tracked in BookingHistory.
  • Reuse the core. Bookings inherit checkout, payments, the order model (ADR 0034), tenancy (ADR 0020), discounts and generic CRUD from the commerce platform rather than a parallel stack.
  • A fuller standalone scheduler also exists — in commerce-worker, on its own database. commerce-worker carries a separate appointment system: its own Api.Main.Repo with appointments, waitlist_entries and blocked_slots migrations plus providers/clients/services/locations schemas and context. It is built but is not the commerce-integrated path — it does not ride the commerce checkout/order pipeline, and its public /book/:slug web layer is absent. So the two models coexist: the thin variant-reservation booking is the one wired into commerce; the standalone scheduler is a parallel subsystem.

Consequences

  • Positive: one platform serves retail and bookings; a booking flows through the same checkout, payment, discount and order machinery for free — no second commerce stack to maintain.
  • Neutral / to revisit: modeling a service as a product variant keeps true scheduling concepts (staff availability windows, locations, waitlists, blocked slots) thin or absent. Businesses needing rich scheduling would outgrow the variant-reservation model, which is where the (deferred) standalone design would return.
  • Neutral / to revisit: two booking models coexist — the thin variant-reservation one (commerce-api, rides checkout/payments) and the fuller standalone scheduler (commerce-worker, its own Api.Main.Repo, missing only the public web layer). Which one a new feature should build on is not unified; consolidating or clearly delineating them is open.

Alternatives considered

  • A standalone appointment/scheduling system: actually built in commerce-worker (its own DB: appointments/waitlist/blocked-slots), but not wired into the commerce checkout/order pipeline and missing its public web layer — so the thin catalog-vertical remains the commerce-integrated booking path. The two coexist rather than one being rejected; the thin vertical is preferred for anything that must ride checkout/payments.
  • A separate "bookings" product distinct from catalog items: rejected — making a service a product variant lets bookings ride the entire existing commerce pipeline without new plumbing.
← 0043 Inkress-on-Shopify is two compliance-separated apps sharing one account link 0045 Delivery is a merchant-authored rate matrix; the checkout shipping total is client-supplied →