GoalKick: running a football centre, from the booking to the till
GoalKick is a management platform for football and five-a-side centres that I designed and built for the Casablanca market: a dashboard for the owner, a booking app for players and a public catalogue of pitches — prices in dirhams, and cash on arrival treated as a real payment method.
The problem
Most centres still take bookings by phone and WhatsApp and write them in a notebook: double bookings, no-shows nobody can trace, and no clear view of which slots actually make money.
What I built
- Owner dashboard — pitches, opening hours and price rules; bookings and closures; customers and loyalty; tournaments and academy classes; memberships, point of sale, expenses and payroll; occupancy and revenue reports.
- Player app — iOS, Android and web from one Expo codebase: find a pitch, see real availability, book, split the cost, join pickup matches.
- Public site — marketing pages and a catalogue of centres rendered on the server with Angular SSR, so search engines can read them.
Technical decisions
Double-booking is blocked by the database. A PostgreSQL exclusion constraint covers each pitch and time range. The booking service inserts optimistically and moves to the next pitch when the constraint rejects it; a test fires 12 simultaneous holds at a 3-pitch slot and checks that exactly 3 succeed.
Availability is derived, never stored. Slots are computed from opening hours, slot length and price rules, minus live bookings — so the calendar can never disagree with the bookings it was built from.
Money is integers. Amounts are stored in minor units, with no floating point anywhere near dirhams, and split payments always add back up to the exact total.
Multi-tenant from the start. No owner endpoint accepts a centre id: the tenant comes from the signed token, so there is no parameter to tamper with.
Payments behind one interface. Cash is a first-class provider, and a Moroccan card acquirer (CMI or Payzone) plugs in without touching the booking logic.
Stack
- Java 25
- Spring Boot 4.1
- PostgreSQL 17
- Liquibase
- Angular 22 SSR
- Expo · React Native
- Testcontainers
- Docker
- GitHub Actions
Integration tests run against a real PostgreSQL through Testcontainers, and every merge deploys itself behind Caddy.
See it live
Is your business running on spreadsheets and WhatsApp? Tell me how it works today.