Screens
Key engineering
Under the hood
A single GraphQL API over modular services, with authorisation enforced at field level so six audiences share one schema instead of one API per role.
Relational source of truth with an append-only audit trail and domain events. Recurring schedules are expanded from rules rather than stored as rows, and notifications go through an outbox so a delivery failure cannot lose the event that caused it.
Managed Postgres behind a serverless-friendly API runtime. Web and mobile clients ship independently of the schema, with a generated SDK keeping them in step.
Money is the sharp edge: webhooks arrive twice, late, or out of order, so reconciliation is idempotent and all three billing relationships settle against the provider rather than against an assumption. Belt progression and billing history are auditable because both get disputed years later.
Stack
What it took
- Six audiences, one schema. Owners, coaches, students, parents acting for dependents, association admins and platform staff — access enforced at GraphQL field level through scope-auth, never hidden in the UI and hoped for.
- The retention engine is the product. Attendance streaks with monthly grants and recalculation runs, leaderboard snapshots and milestones feed an at-risk signal, so a coach hears about a fading member while there is still time to act.
- Billing is three systems that have to agree: what the gym owes the platform, what a student owes the gym, and what a family pays as one group — reconciled against Stripe webhooks that arrive twice, late, or out of order.
- Belt progression is auditable — rank history, progression rules and graduation events — because a promotion is a claim someone will dispute years later.
- GDPR as engineering, not a checkbox: data export, real account deletion, privacy controls around sensitive records, and an audit trail over all of it.
- A way in for gyms leaving other software: import mapping templates and row-level import runs, so a messy spreadsheet lands as clean membership data.
