Data
PostgreSQL through Prisma. The model covers 50+ entities and holds the whole sport in one schema - club, team, cheerleader, coach, competition, entry, payment, qualification, certificate, ticket. Changes go through versioned migrations.
The platform that runs an entire national sport - clubs, coaches, cheerleaders, competitions, judging, qualifications, certifications and payments - on one data model. From club registration to competition-day paperwork, with no spreadsheets and no paper forms.
Product design · domain architecture · full-stack build · platform migration
NestJS · Prisma · PostgreSQL · BullMQ · Stripe
2022 · in active development
In production · second generation in build

The UK Cheerleading Association runs an entire sport: community clubs and school programmes, coaches and cheerleaders, competitions and judging, coaching qualifications, exams and certifications. For years all of it lived in spreadsheets, email threads and paper forms that didn't talk to each other.
Clubs registered by hand. Competition entries were reconciled manually, and every roster change after the deadline meant another round of emails and another version of the spreadsheet. The running order was assembled in Excel, score sheets were printed from that same Excel, and judges were handed their paperwork in folders. Coaching certificates were issued and verified on trust. Payments arrived through a patchwork of methods and rarely matched who owed what.
And sensitive records for school cheerleaders - children under 18 - needed real protection, not a shared folder.
What was needed was a platform that knows the rules of the sport itself, that carries a club from entry to a clean invoice and from the running order to a signed score sheet - reliably, repeatably, and without anyone re-keying a thing.
We built a domain-driven platform where the rules of the sport live in the code, not in the heads of a handful of people. One data model, one place of truth, and a flow that moves forward - from club, through competition, to certificate.






The backend is a NestJS application split by domain - each domain has its own module, controller and service, and the business rules live in the services. Controllers stay thin: validate, call, shape the response.
PostgreSQL through Prisma. The model covers 50+ entities and holds the whole sport in one schema - club, team, cheerleader, coach, competition, entry, payment, qualification, certificate, ticket. Changes go through versioned migrations.
BullMQ on Redis separates slow work from the HTTP request. Sending an email is a job, not a block - with retries and a record of failed jobs.
Google Cloud Storage with signed URLs. Files upload straight into storage and are read through a time-limited signed link, so private documents never become public.
JWT authentication (local and Google), roles and policies at route level, and record ownership as an extra layer - a club sees its own club, not somebody else's.
React and Vite, MUI with data grids, Redux Toolkit for state, react-hook-form for forms, i18next for languages, dnd-kit for assembling the running order by dragging, the Stripe Terminal SDK for the card reader.
The first generation of the platform ran on the Strapi headless CMS, and it got the sport moving. When the scope outgrew what a CMS can sustain, rather than rewriting from scratch we built a second generation on NestJS and Prisma - and we are migrating it domain by domain, with the application running the whole time.
The migration is not run “by feel”. Every domain has its own script with three modes: a dry run that changes nothing, --save which writes a report of what would happen, and --apply which performs the move. Alongside it comes an audit script that compares the old and new state, a behaviour-parity report, and a cutover runbook - step by step, with a way back if it is needed.
What moved includes what nobody sees: media files from Cloudinary to GCS, certificates, historical payments, competition entries, users and roles.
The platform is in production and runs the sport day to day. Clubs register themselves and enter competitions without back-and-forth. Competition day assembles from the running order, and judges get paperwork that generates itself. Coaches earn qualifications and walk away with a certificate anyone can check in the register. Schools are invoiced cleanly, community clubs pay by card, and whatever is left is settled with a card reader at the competition itself - all in the same payment ledger.
In parallel, the second-generation backend on NestJS and Prisma is taking over domain by domain, with audit scripts and a runbook for every step.
“TherraCode built the system our sport never had. Clubs, competitions and coaching qualifications finally live in one place - and we trust it.”
Tell us what you're building. We'll come back within one business day with a clear plan, a timeline, and a real human to talk to.