Back to Home

One tender, three modules: answering a real sports-centre spec by composition

A university sports centre put out a procurement spec. Instead of building an app, we composed one — from three single-concern modules that now live in the Constellation for the next buyer.

Procurement documents are a gift, because nobody writes them to be impressed. This one came from a university sports centre operating across two sites, and it reads like every operations system you've ever inherited: block bookings that copy across terms, clash detection, pricing with a manual override, a debtor pop-up at the front desk, invoice batch runs, per-customer statements, gym memberships with door access control, footfall reports, missed-payment reminders.

The obvious response is a bespoke build — one application, one codebase, one client. We did something different: we split the spec into three single-concern modules, built each one lean, and let them compose. The tender gets answered; the catalogue gets three reusable units; the next sports centre — or dance studio, or invoicing SaaS, or climbing gym — starts from working code instead of a blank file.

The spec, mapped

The tender asked for Module How it's covered
Multi-site facility hire, clash detection, class capacity, price override, weekly block bookings copied across terms, day diary, debtor pop-up, Excel round-trip Facility Bookings Pure booking engine: half-open overlap clash checks on exclusive resources, attendee-sum capacity on classes, rate×duration pricing with override-wins, term blocks that flag clashing dates but confirm the rest, CSV import/export
Invoicing with editable drafts, batch runs, cash/cheque/card payments, debtors list, per-customer statements Invoicing & Debtors Draft invoices with line editing locked at issue, automatic references and 30-day terms, batch issue with per-invoice skip reasons, payments that settle invoices, a live debtors list, statements per customer
Gym membership records, payments, access control, footfall, missed-payment and expiry reminders, mail merge Gym Memberships Membership types, monthly payments that extend cover and reactivate lapsed members, door check-in that refuses unpaid or cancelled members and records why, daily footfall, a renewals-due feed with the fee owed — ready for mail merge

Where it stops being three apps

Splitting a spec into modules only works if the modules actually talk to each other. Two kinds of connection do the work here, and neither is a copy-paste.

Shared services, referenced — not duplicated. All three modules audit their mutations and send templated email. None of them contains an audit log or a mailer. Each one references the canonical AuditLogService and MailService from the Constellation's Audit Log and Transactional Email modules — lightweight reference edges that resolve to the original records at assembly time. Every booking, invoice, payment, enrolment and cancellation across the three modules lands in one audit trail, and every confirmation email goes through one delivery pipeline, because it is literally the same code. Six new reference edges into two shared services, zero regenerated lines. (Why referencing beats regenerating, with measurements: the economics of code reuse.)

A cross-module bridge. The tender's front desk wants unpaid bookings chased as invoices. So Invoicing & Debtors references the Facility Bookings data model and exposes one action: bill a customer's unpaid facility bookings. It pulls every unpaid, uncancelled booking onto a draft invoice — one line per booking, with the facility, date and times — and marks the bookings invoiced so they drop off the facility desk's debtor pop-up. In the live demo data this has already happened: a debating society's unpaid room hire from the bookings module is sitting on the billing dashboard as an issued invoice on the debtors list. Two modules, one workflow, no glue app in between.

Built lean, proven live

Each module is a pure service engine (the rules, fully testable), a thin desk controller (validation plus the audit and mail side effects), and a server-rendered dashboard. Twenty-five studio tests run against the trio — clash rules, block copies, invoice edit-locking, payment settlement, the cross-module billing bridge, door access control, footfall, reminder feeds — and each module's demo data is seeded and rendering on its live screen: today's bookings and an active term block on the facilities desk, a part-paid debtor and the bridged invoice on the billing dashboard, and a member roster with active, lapsed and cancelled states behind the gym's door check-in.

Honesty about the boundary, as always: the tender also asks for things a code catalogue shouldn't pretend to ship — chip-and-pin hardware, an Oracle Fusion finance feed, a member phone app, document uploads. Those are integration and product work on top. What the modules cover is the operational core the rest plugs into — and that core is the part every operator otherwise rebuilds from scratch.

The point

A bespoke build would have answered this tender once. Composition answered it and left the catalogue three modules richer — each one single-concern, tested, and referenced into real workflows rather than sitting in a samples folder. The next project that needs bookings, billing or memberships doesn't pay to generate them again; it references what's already proven and pays only for the wiring.

That's the loop Stellify is built around: connect your editor over MCP, let the AI find and wire existing code first, and generate only what doesn't exist yet. Browse what's already there in the Constellation — or try it free, 20 AI messages a month, no card: stellisoft.com.