VBRO Technology
New

AI chatbots, WhatsApp automation and workflow agents — See the reference architectures we deploy.

See our AI solutions
Engineering

Building GoLibora: what a study-library SaaS taught us about multi-tenancy

GoLibora runs 100+ study libraries on one codebase. The decisions that made that possible — and the ones we would make again — from seat modelling to tenant isolation.

VA VBRO Administrator 4 min read
GoLibora library management platform shown on desktop, tablet and mobile

GoLibora is our own product: a SaaS platform that runs Indian study libraries — the reading rooms where students book a seat by the month to prepare for competitive exams. Today it runs 100+ libraries and 1,000+ seats from one codebase. This is a write-up of the engineering decisions that made that workable, because most of them generalise to any multi-tenant product.

The domain looks simple. It is not.

A study library sells one thing: a seat, for a period of time. But every owner sells it differently — full-day seats, morning and evening shifts, hourly slots, monthly passes, and combinations of all four across branches. The first modelling decision was the one everything else stood on: the seat is the unit of business, not the room. A seat belongs to a branch, carries its own slot-wise pricing, and a booking is a claim on a seat for a slot and a period.

  • Occupancy becomes a query, not an estimate from a wall chart
  • Revenue per seat — the number owners actually run the business on — falls out for free
  • Double-allotment is structurally impossible rather than procedurally discouraged
  • A renewal is just another claim on the same seat, so history is never overwritten

Multi-tenancy: shared schema, scoped queries

At a few hundred rupees a month per library, per-tenant databases and per-tenant deploys were never on the table — the economics demand one deploy serving everyone. We went with a shared schema and tenant scoping enforced at the query layer, so a request can only ever see its own library's rows.

// Every tenant-owned model carries this scope. Forgetting a WHERE
// clause is a bug you write once; a global scope makes it a bug
// you cannot write at all.
protected static function booted(): void
{
    static::addGlobalScope('tenant', function (Builder $query) {
        $query->where('library_id', app(TenantContext::class)->id());
    });
}

Branding is tenant data, not configuration. Each library gets its own subdomain and its own branded public site out of the same codebase — the free website turned out to be one of the strongest selling points, and it cost us one wildcard DNS entry and a theme table.

Payments: UPI-first, idempotent always

Fees here are small, frequent and overwhelmingly UPI. We integrated UPI, Razorpay, Stripe and PhonePe behind one internal payments interface, and made a rule that has never been broken: every gateway webhook is processed idempotently. Gateways retry; networks duplicate; a fee credited twice destroys an owner's trust in every number on the dashboard. Each webhook carries an event key, and applying the same key twice is a no-op by design.

GST was the other non-negotiable. Invoices split CGST and SGST correctly on every receipt, because the alternative is an owner's accountant finding out at filing time — the most expensive possible moment.

QR attendance and the parent portal

Attendance by QR check-in sounds like a gimmick until you see what it feeds: parents get a portal showing attendance and dues in real time, which quietly removed the single largest category of front-desk phone calls. The lesson we keep re-learning: the feature is rarely the capture; it is what the captured data lets someone stop doing.

What we would do again

  • Model the smallest billable unit first — everything reports off it
  • Enforce tenancy in one place, at the query layer, and test it adversarially
  • Treat webhook idempotency as a founding rule, not a hardening task
  • Make branding tenant data so every customer feels like the only customer
  • Run the product ourselves before selling it — operating GoLibora is where half the roadmap comes from
A multi-tenant product is a promise that no tenant will ever know the others exist — in data, in branding, and in performance.
— VBRO engineering
Share this

Comments

Be the first to comment

Every comment is read before it appears.

Never published. Used only if we need to reply.

Reviewed before publishing.

Working on something like this?

If this is the kind of problem you are facing, we would be glad to talk it through.

Where we work

582 cities across 19 countries.

See all locations