Skip to content

Reference build

Meridian

Two-sided marketplace

The hardest product shape we build: two audiences, inventory that exists in time as well as in space, and money moving between strangers.

Year
2026
Engagement
Reference build

What it had to do.

Equipment owners list machines with the dates they are free. Contractors search by category, location and date range, then book. Money is authorised at booking and released only once both sides confirm handover. Two separate products, a supplier console and a renter marketplace, sharing one availability model.

The constraint

Inventory here exists in time, not just in stock. A double booking is not an inconvenience, it is a contractor arriving at a site to find the machine gone. Every design decision below follows from making that failure structurally impossible rather than merely unlikely.

Architecture

How it is put together.

Clients

  • MarketplaceNext.js App Router, SSR search
  • Supplier consoleNext.js, authenticated

Edge

  • CDN + image transformCloudflare
  • Rate limitingper IP and per account

Services

  • Availabilityrange arithmetic, holds
  • Bookingstate machine
  • Paymentsauth / capture / payout
  • Notificationsqueue worker

Data

  • PostgreSQLsource of truth, GiST exclusion
  • Redisshort-lived holds, cache
  • Meilisearchderived index
  • Object storagelisting media

External

  • Stripe Connectsplit payouts
  • Email + SMStransactional
Availability and PostgreSQL are highlighted: the booking guarantee lives in the database, not in application code.

Stack

  • Next.js
  • PostgreSQL
  • Prisma
  • Redis
  • Stripe Connect
  • Meilisearch

Every choice cost something.

An architecture without trade-offs is an architecture nobody has stress-tested. Here is what we gave up for what we got.

Double booking is rejected by the database

What we did

Availability is stored as `tstzrange` with a GiST exclusion constraint on (listing_id, period).

Why

Application-level checks are a race waiting to happen: two requests both read 'free', both write. An exclusion constraint makes overlapping ranges impossible to commit, whatever the application does.

What it cost

Sub-hour granularity gets awkward, so bookings are capped at one-hour resolution. For equipment hire that is the right trade; for a meeting-room product it would not be.

Search is disposable, PostgreSQL is not

What we did

Meilisearch holds a derived index rebuilt from PostgreSQL; nothing is ever read from it as truth.

Why

Search engines are wonderful and occasionally lose data. Treating the index as a cache means a corrupted index is a rebuild, not an incident.

What it cost

New listings can be a few seconds behind in public search. Suppliers are served their own listings from PostgreSQL, so it never looks broken to the person who just published.

Money is authorised at booking, captured at handover

What we did

Stripe Connect with manual capture, plus a separate charge path for long bookings.

Why

Neither side trusts the other yet. Holding an authorisation gives the renter certainty and the supplier protection without us holding funds.

What it cost

Card authorisations expire after about seven days, so bookings further out cannot rely on the original hold. Those are re-charged at handover. More moving parts, but honest about what a card hold actually guarantees.

How it was built.

  1. Weeks 1-2

    Discovery and data model

    Availability model, booking state machine and payment flow agreed on paper before any UI. This is where the exclusion-constraint decision was made.

  2. Weeks 3-6

    Supply side

    Supplier onboarding, listing management, calendar and media pipeline. Suppliers first: a marketplace with no inventory has nothing to test.

  3. Weeks 7-10

    Demand side and booking

    Search, filtering, date-range selection, hold-and-confirm flow, messaging between parties.

  4. Weeks 11-13

    Payments and payouts

    Connect onboarding, authorisation, capture, refunds, disputes and the reconciliation report nobody asks for until they need it.

  5. Week 14

    Hardening

    Load testing on the availability path, rate limits, audit logging, runbooks and handover documentation.

What is true of it.

Properties of the build we can demonstrate, not business results we did not measure.

  • Overlapping bookings cannot be committed: the constraint is enforced by PostgreSQL, so no application path can bypass it.

  • The search index can be dropped and rebuilt from the primary database without data loss.

  • Payment state is reconstructable from an append-only event log, which is what makes disputes answerable.

  • Documented with architecture decision records covering each of the three trade-offs above.

Next step

Tell us what needs building.

A 30-minute call, under mutual NDA if you want one signed first. You leave with a view on scope, team shape and budget range, whether or not you work with us.

Or write to hello@warmbench.dev