projekti/Sport · Platforma

UKCA

Platforma koja vodi cijeli nacionalni sport - klubovi, treneri, cheerleaderi, natjecanja, suđenje, kvalifikacije, certifikati i naplata - na jednom modelu podataka. Od prijave kluba do dokumentacije natjecateljskog dana, bez tablica i papirnatih obrazaca.

Uloga

Dizajn proizvoda · domenska arhitektura · full-stack izrada · migracija platforme

Tehnologije

NestJS · Prisma · PostgreSQL · BullMQ · Stripe

Godina

2022 · u aktivnom razvoju

Status

U produkciji · druga generacija u izradi

0+Domenskih entiteta u jednom modelu podataka
0Načina naplate, jedna knjiga plaćanja
0Ručnih koraka: prijava → naplata → dokumentacija natjecateljskog dana
Izazov

Što je trebalo riješiti.

UK Cheerleading Association vodi cijeli sport: društvene klubove i školske programe, trenere i cheerleadere, natjecanja i suđenje, trenerske kvalifikacije, ispite i certifikate. Sve je to godinama živjelo u tablicama, e-mail nizovima i papirnatim obrascima koji međusobno nisu komunicirali.

Klubovi su se prijavljivali ručno. Prijave za natjecanja usklađivale su se ručno, a svaka izmjena sastava tima nakon roka značila je novi krug dopisivanja i novu verziju tablice. Redoslijed nastupa slagao se u Excelu, ocjenjivački listovi ispisivali su se prema tom istom Excelu, a sucima se dokumentacija dijelila po mapama. Trenerski certifikati izdavali su se i provjeravali na povjerenje. Plaćanja su stizala raznim metodama i rijetko se poklapala s time tko je što dužan.

A osjetljivi podaci o školskim cheerleaderima - djeci mlađoj od 18 godina - trebali su stvarnu zaštitu, ne dijeljenu mapu.

Trebala je platforma koja pravila sporta zna sama, koja nosi klub od prijave do fiskalno urednog računa i od redoslijeda nastupa do potpisanog ocjenjivačkog lista - pouzdano, ponovljivo i bez ičijeg ručnog prepisivanja.

Naš pristup

Kako smo to izgradili.

Izgradili smo domenski vođenu platformu u kojoj pravila sporta žive u kodu, a ne u glavama nekolicine ljudi. Jedan model podataka, jedno mjesto istine, i tijek koji ide naprijed - od kluba, preko natjecanja, do certifikata.

  • Pravila sporta u kodu, ne u podnožju obrasca. Dobne skupine, kategorije, masters i crossover pravila provjeravaju se na backendu prije nego što prijava uopće postane prijava. Nemoguć sastav tima jednostavno se ne može poslati - nema naknadnog usklađivanja e-mailom.
  • Dva svijeta klubova, jedan sustav. Društveni klubovi i školski programi imaju različite uloge, različite sastave i različite načine plaćanja, ali dijele isti model podataka. Škole dobivaju brendirane fakture s narudžbenicom i Sage referencom, spremne za računovodstvo; društveni klubovi plaćaju karticom.
  • Knjiga plaćanja, ne popis transakcija. Svaka prijava vodi vlastiti saldo kroz djelomične uplate, povrate i naknadne izmjene sastava. Uplata se raspoređuje na ono što pokriva, a promjene u prijavi ostavljaju revizijski trag - tko je što promijenio i kada. Stripe Checkout online, Stripe Terminal za plaćanje karticom uživo na natjecanju, faktura za škole. Sve tri završe u istoj knjizi.
  • Natjecateljski dan iz jednog izvora. Redoslijed nastupa je izvor istine. Iz njega se generira sva papirologija koja je do tada nastajala ručno: ocjenjivački listovi raspoređeni po sucima, packing liste i kuverte - kroz ExcelJS i konverziju u PDF, spremni za ispis.
  • Cjelovit obrazovni tijek. Trenerski tečajevi i prijave, online kvizovi i ispiti s pitanjima po sekcijama i evidencijom pokušaja, kvalifikacije po disciplini, razini i rangu, te tečajevi obnove znanja s tijekom pregleda i odobrenja. Na kraju stoji certifikat i javni registar certificiranih trenera - klub ili roditelj u svakom trenutku može provjeriti stoji li iza trenera stvarna kvalifikacija.
  • Ulaznice koje se skeniraju. Kupnja ulaznica za gledatelje s QR propusnicama po sesijama, provjeravanima na ulazu - umjesto liste na papiru.
  • Enkripcija u mirovanju za maloljetnike. Osjetljiva polja školskih cheerleadera šifriraju se AES-256 prije zapisa u bazu, s ključem koji nikad ne napušta backend. Frontend te podatke nikad ne vidi u sirovom obliku, a tko ih dobije, dobiva ih kroz ulogu koja na to ima pravo.
  • Otporno na spor SMTP. Slanje e-mailova ide kroz trajan red poslova s ponovnim pokušajima - korisnik dobiva odgovor odmah, poruka odlazi kad odleti. Predlošci se renderiraju kroz React Email, pa se izgled poruke mijenja bez diranja logike koja je šalje.
Odabrani zasloni
Manage your club
Your club overview
Club cheerleaders table
Team management
Assign team member, with age eligibility rules
Enter competition
Arhitektura

Jedan model podataka, jasne granice.

Backend je NestJS aplikacija podijeljena po domenama - svaka domena ima svoj modul, kontroler i servis, a poslovna pravila žive u servisima. Kontroleri ostaju tanki: validiraj, pozovi, oblikuj odgovor.

01

Podaci

PostgreSQL preko Prisme. Model pokriva 50+ entiteta i drži cijeli sport u jednoj shemi - klub, tim, cheerleader, trener, natjecanje, prijava, plaćanje, kvalifikacija, certifikat, ulaznica. Promjene idu kroz verzionirane migracije.

02

Red poslova

BullMQ na Redisu odvaja spor rad od HTTP zahtjeva. Slanje e-maila je posao, ne blokada - s ponovnim pokušajima i evidencijom neuspjelih poslova.

03

Pohrana

Google Cloud Storage s potpisanim URL-ovima. Datoteke se učitavaju izravno u pohranu i čitaju kroz vremenski ograničen potpisan link, pa privatni dokumenti nikad ne postaju javni.

04

Pristup

JWT autentikacija (lokalna i Google), uloge i pravila na razini rute, te vlasništvo nad zapisom kao dodatni sloj - klub vidi svoj klub, a ne tuđi.

05

Frontend

React i Vite, MUI s podatkovnim tablicama, Redux Toolkit za stanje, react-hook-form za obrasce, i18next za jezike, dnd-kit za slaganje redoslijeda nastupa povlačenjem, Stripe Terminal SDK za čitač kartica.

Migracija

Zamjena motora u vožnji.

Prva generacija platforme radila je na Strapi headless CMS-u i pokrenula je sport. Kad je opseg prerastao ono što CMS može održati, umjesto prepisivanja od nule izgradili smo drugu generaciju na NestJS-u i Prismi - i selimo domenu po domenu, uz aplikaciju koja cijelo vrijeme radi.

Migracija se ne izvodi “na osjećaj”. Svaka domena ima vlastitu skriptu s tri načina rada: dry-run koji ništa ne mijenja, --save koji zapisuje izvještaj o tome što bi se dogodilo, i --apply koji izvodi seobu. Uz to ide revizijska skripta koja uspoređuje staro i novo stanje, izvještaj o paritetu ponašanja, i runbook za prelazak na novo - korak po korak, s povratkom unatrag ako zatreba.

Preseljeno je i ono što se ne vidi: medijske datoteke s Cloudinaryja na GCS, certifikati, povijesna plaćanja, prijave na natjecanja, korisnici i uloge.

Sigurnost

Najmanje ovlasti, ništa vani.

  • Enkripcija u mirovanju (AES-256) za osjetljiva polja školskih cheerleadera; ključ je isključivo backend tajna i nikad ne dolazi do preglednika.
  • Lozinke se pohranjuju kao bcrypt hash; tokeni za reset lozinke su jednokratni i vremenski ograničeni.
  • Uloge i vlasništvo provjeravaju se na svakoj ruti, ne u sučelju - sučelje skriva, backend zabranjuje.
  • Privatni dokumenti u pohrani dostupni su isključivo kroz potpisane URL-ove s rokom trajanja.
  • Tajne i ključevi dolaze iz okruženja i upravitelja tajni, nikad iz repozitorija.
  • Stripe se nikad ne vjeruje na riječ: uplata postaje uplata tek kad je potvrdi webhook, s provjerom potpisa.
Tehnologije

Odabrano zbog trajnosti.

Okruženje / jezikNode.js 20 · TypeScript 5
FrameworkNestJS 11
Baza / ORMPostgreSQL 16 · Prisma 6 (43 migracije, 1500 linija sheme)
Red poslovaBullMQ + Redis 7
PlaćanjaStripe - Checkout, webhookovi, Terminal (card_present)
Dokumentipdfkit · ExcelJS · konverzija XLSX → PDF · qrcode
E-mailReact Email predlošci · Nodemailer / SMTP
PohranaGoogle Cloud Storage · potpisani URL-ovi
SigurnostPassport JWT · bcrypt · AES-256 enkripcija polja
FrontendReact · Vite · MUI · Redux Toolkit · i18next
Lokalni razvojDocker Compose (Postgres + Redis)
Inženjerska kvaliteta

Izgrađeno za predaju.

  • TypeScript u strogom načinu, s validacijom ulaza kroz class-validator i Zod prije nego što podatak dotakne servis.
  • Jedinični testovi na mjestima gdje pravila stvarno bole: računanje dobnih skupina, raspored sudaca po ocjenjivačkim listovima, mapiranje redoslijeda nastupa, generiranje faktura.
  • Verzionirane migracije baze; nijedna promjena sheme ne ide ručno.
  • Migracijske skripte s obveznim dry-runom, izvještajem i revizijom prije bilo kakvog upisa u stvarne podatke.
  • Dokumentiran cutover runbook i izvještaj o paritetu ponašanja između generacija platforme.
  • Docker Compose za lokalni razvoj - Postgres i Redis dižu se jednom naredbom.
Status

Gdje se nalazi.

Platforma je u produkciji i vodi sport iz dana u dan. Klubovi se sami prijavljuju i ulaze u natjecanja bez dopisivanja. Natjecateljski dan slaže se iz redoslijeda nastupa, a suci dobivaju dokumentaciju koja se generira sama. Treneri stječu kvalifikacije i odlaze s certifikatom koji svatko može provjeriti u registru. Škole se uredno fakturiraju, društveni klubovi plaćaju karticom, a ono što ostane rješava se čitačem kartica na samom natjecanju - sve u istoj knjizi plaćanja.

Usporedo, druga generacija backenda na NestJS-u i Prismi preuzima domenu po domenu, uz revizijske skripte i runbook za svaki korak.

u produkciji · nacionalna udruga
  • prijava i upravljanje klubom
  • prijave na natjecanja (društvene + školske)
  • validacija dobi, kategorija i crossovera
  • Stripe online · Terminal uživo · fakturiranje škola
  • knjiga plaćanja sa saldom i revizijskim tragom
  • redoslijed nastupa i dokumentacija natjecateljskog dana
  • online ispiti, kvalifikacije i certifikati
  • ulaznice s QR propusnicama
  • enkripcija podataka maloljetnika u mirovanju
  • migracija na drugu generaciju platforme
“TherraCode je izgradio sustav kakav naš sport nikad nije imao. Klubovi, natjecanja i trenerske kvalifikacije napokon žive na jednom mjestu - i vjerujemo mu.”
UK Cheerleading Association
Sljedeći projekt

The Playbook Studio

OTVORENI ZA NOVE PROJEKTE

Imaš sličan projekt?

Reci nam što gradiš. Javljamo se u roku od jednog radnog dana s jasnim planom, rokovima i pravom osobom za razgovor.