Obsah kapitoly
Entitný diagram
Nasledujúci diagram ukazuje hlavné entity systému a ich vzťahy. Affiliation je uprostred zámerne — takmer všetko v systéme sa ňou preplieta. Nie je to technický "join table", ale prvotriedna biznis entita s vlastným životným cyklom.
Person — identita osoby
Person je stabilná entita, ktorá opisuje človeka ako ľudskú bytosť, nie jeho športovú rolu. Žiadne športové polia v Person tabuľke nie sú — kluby, licencie, role, všetko patrí do iných entít.
Povinné jadro
| Pole | Typ | Popis |
|---|---|---|
person_id | UUID | Primárny kľúč, nemenný, interný |
given_name | String | Krstné meno |
family_name | String | Priezvisko |
date_of_birth | Date | Dátum narodenia |
gender | Enum | male / female / other / undisclosed |
nationality | ISO 3166 | Štátna príslušnosť |
verification_status | Enum | unverified / self_declared / verified_by_org / verified_by_eid / verified_by_rfo |
date_of_death | Date · nullable | Dátum úmrtia (notifikácia z RFO) |
Identifikátory
| Pole | Typ | Popis |
|---|---|---|
rfo_identifier | String · encrypted | IFO z RFO, len pri verifikácii |
national_id_hash | Hash | Hash rodného čísla pre deduplikáciu |
national_id_encrypted | Encrypted blob | Rodné číslo — dešifruje sa len pri komunikácii s RFO |
foreign_id_type | Enum · nullable | passport / residence_permit — pre cudzincov |
foreign_id_value | String · encrypted | Hodnota zahraničného identifikátora |
foreign_id_country | ISO 3166 | Krajina vydania |
Prečo UUID a nie rodné číslo
Rodné číslo nie je vhodný primárny kľúč. Cudzinci ho nemajú. Môže sa zmeniť. A hlavne — je to citlivý osobný identifikátor, ktorý nemá cestovať v API odpovediach a logoch. UUID sú stabilné a bez sémantickej hodnoty.
Affiliation — srdce systému
Affiliation je vzťah osoby k organizácii v určitej role (druhu športovej činnosti), v určitom športe a disciplíne, platný v určitom časovom intervale. Je to najdôležitejšia entita celého systému a modeluje sa event-sourcovane.
Schéma
| Pole | Typ | Popis |
|---|---|---|
affiliation_id | UUID | Primárny kľúč |
person_id | UUID · FK | Ktorá osoba |
organization_id | UUID · FK | Ktorá organizácia |
activity_code | String · FK | Z číselníka druhov športovej činnosti |
legal_basis_code | String · FK | Z číselníka právnych titulov |
sport_code | String · FK | Z číselníka športov |
discipline_code | String · FK · nullable | Z číselníka disciplín |
category_code | String · nullable | Veková / výkonnostná kategória |
valid_from | Date | Začiatok platnosti |
valid_to | Date · nullable | Koniec platnosti (null = otvorený) |
status | Enum | pending / active / suspended / terminated |
registration_number | String · nullable | Interné ID pridelené organizáciou |
source_app_id | UUID | Certifikovaná aplikácia, ktorá záznam vytvorila |
Kľúčové vlastnosti
- Temporálnosť je vstavaná. Aktuálnosť sa odvodzuje z
valid_from ≤ now() ≤ COALESCE(valid_to, ∞)astatus = active. - Každá zmena je event. Prestup, pozastavenie, ukončenie nie je UPDATE, ale samostatná udalosť.
- Kompozitné constrainty. Osoba nemôže mať dve aktívne "tréner" afiliácie v tom istom klube a športe súčasne.
- Prestup = ukončenie + nový začiatok. Zachováva históriu.
Organization — hierarchia subjektov
Organizácie nie sú ploché. Typológia pokrýva celý ekosystém — od národných zväzov cez samosprávu, štátnu správu, školstvo až po komerčné subjekty. Kompletný zoznam je v kapitole Číselníky.
Schéma
| Pole | Typ | Popis |
|---|---|---|
organization_id | UUID | Primárny kľúč |
org_type | String · FK | Z číselníka druhov organizácií |
legal_name | String | Úplný právny názov |
ico | String · nullable | IČO (ak má) |
legal_form_code | String · FK | Právna forma (z RPO číselníka) |
rpo_identifier | String · nullable | Identifikátor v RPO |
rpo_registered | Boolean | Či je v RPO |
parent_org_id | UUID · nullable | Nadradená organizácia (zväz → regionálny zväz) |
address | Object | Sídlo |
valid_from | Date | Vznik |
valid_to | Date · nullable | Zánik |
status | Enum | active / suspended / dissolved |
Jedna organizácia, viac športov
Polyvalentný klub môže byť akreditovaný vo viacerých športoch súčasne. Preto vzťah Organization × Sport × Discipline je samostatná entita ORG_SPORT_ACCREDITATION s vlastnou platnosťou a stavom uznania.
Facility — športovisko
Športovisko je v SportUp prvotriedna entita — nie atribút podujatia alebo klubu. Dôvod: jedno športovisko slúži viacerým subjektom súčasne, má vlastnú prevádzkovú a technickú identitu, jeho dáta sú zároveň podkladom pre agendu cestovného ruchu.
Schéma
| Pole | Typ | Popis |
|---|---|---|
facility_id | UUID | Primárny kľúč |
label_sk | String | Názov športoviska |
facility_type | String · FK | Druh podľa číselníka |
owner_organization_id | UUID · FK | Vlastník |
operator_organization_id | UUID · FK · nullable | Prevádzkovateľ ak je iný |
sports_supported | Array<sport_code> | Aké športy sú tu možné |
disciplines_supported | Array<discipline_code> | Konkrétne disciplíny |
location | Object | Adresa, obec, kraj, GPS súradnice |
capacity | Integer · nullable | Kapacita pre divákov |
surface | Enum | Prírodná tráva, umelá tráva, tartan, parkety, ľad... |
indoor_outdoor | Enum | indoor / outdoor / both |
features | Array | Šatne, parkovanie, bufet, bezbariérový prístup... |
licensed_for_competitions | Array | Úroveň akreditácie pre súťaže |
access_model | Enum | Verejný voľný / platený / členský / komerčný |
tourism_tags | Array | Príznaky pre cestovný ruch |
opening_hours | Object | Otváracie hodiny, sezónnosť |
status | Enum | active / under_construction / temporarily_closed / demolished |
Prečo samostatná entita
- Jedno športovisko, viac subjektov. Obecný štadión využíva klub, škola, ochotnícke ligy, komerčné nájmy.
- Vlastník ≠ prevádzkovateľ. Obec vlastní, klub prevádzkuje. Model to rozlišuje explicitne.
- Podujatia sa odohrávajú tu. Event Participation referencuje
facility_id. - Cestovný ruch. Register je verejne dotazovateľný cez otvorené API — aplikácie typu "lyžiarske strediská v Žilinskom kraji" alebo "najbližšia plaváreň" čerpajú dáta odtiaľto.
- Dotácie a investície. Ministerstvo vie, kde sú športoviská, v akom stave, s akou kapacitou. Dotačná politika nestojí na ad-hoc prieskumoch.
Vzťahy k ostatným entitám
Facility ↔ Organization
M:N cez entitu FACILITY_USAGE — ktoré organizácie majú pravidelnú prevádzku, akú a v akom časovom okne.
Facility ↔ Event
Podujatie sa koná na jednom alebo viacerých športoviskách. Entita EVENT_VENUE s časovým rozvrhom využitia.
Facility ↔ Location
Povinná referencia na číselník obcí, okresov a krajov. GPS súradnice sú povinné — bez nich nie je možné využitie v mapových službách.
Facility ↔ Person
Priame vzťahy neexistujú. Osoba je spojená so športoviskom len cez Organization (prevádzkovateľa) alebo cez Event Participation.
Activity — druh športovej činnosti
Druh športovej činnosti (napr. amatérsky športovec, tréner, rozhodca, usporiadateľ, dobrovoľník) je hierarchický číselník. Každá Affiliation má práve jeden activity_code — ten istý človek v klube môže mať dve afiliácie, ak je zároveň hráčom aj trénerom.
Konkrétne druhy činností, ich kódy a popisy nájdete v kapitole Číselníky → Druhy športovej činnosti.
Qualification — licencie v čase
Kvalifikácia je napríklad trénerská licencia UEFA Pro, rozhodcovská licencia, športový lekár atestácia. Kvalifikácia je viazaná na osobu, nie na afiliáciu. Tréner s licenciou UEFA B si ju nesie so sebou aj keď zmení klub. Oprávnenie trénovať konkrétny tím je však kombináciou: Person + aktívna Qualification + aktívna Affiliation ako tréner.
| Pole | Typ | Popis |
|---|---|---|
qualification_id | UUID | Primárny kľúč |
person_id | UUID · FK | Držiteľ |
qualification_type | String · FK | Z číselníka kvalifikácií |
issuing_organization_id | UUID · FK | Kto vydal |
level | String · nullable | Stupeň (Pro, A, B, C...) |
issued_at | Date | Dátum vydania |
valid_from | Date | Začiatok platnosti |
valid_to | Date | Koniec platnosti |
status | Enum | active / expired / revoked / suspended |
document_id | UUID · FK | Scan dokladu |
Consent — súhlas a legal basis
Consent je samostatná entita, nie JSON pole niekde na Person. Modeluje sa granulárne: jeden záznam na účel × organizáciu alebo aplikáciu × osobu.
| Pole | Typ | Popis |
|---|---|---|
consent_id | UUID | Primárny kľúč |
person_id | UUID · FK | Dotknutá osoba |
purpose_code | String · FK | Z Purpose Catalogue |
purpose_version | Integer | Verzia účelu v čase udelenia |
legal_basis | Enum | consent / contract / legal_obligation / vital_interests / public_task / legitimate_interest |
granted_to_type | Enum | organization / app / public_sector |
granted_to_id | UUID | Konkrétny prijímateľ |
granted_at | Timestamp | Čas udelenia |
granted_by | UUID | Kto udelil (osoba alebo zákonný zástupca) |
withdrawn_at | Timestamp · nullable | Čas odvolania |
Detailný popis Purpose Catalogue nájdete v kapitole Účely spracovania.
Katalóg eventov pre Affiliation
| Event | Kedy sa produkuje | Stav po evente |
|---|---|---|
AffiliationRegistered | Nová registrácia osoby v organizácii | pending |
AffiliationActivated | Overenie prešlo | active |
AffiliationSuspended | Pozastavenie (zranenie, disciplinárka, admin) | suspended |
AffiliationResumed | Návrat z pozastavenia | active |
AffiliationActivityChanged | Zmena druhu činnosti v tej istej organizácii | active |
AffiliationTerminated | Koniec vzťahu | terminated |
AffiliationCorrected | Oprava chybného záznamu | bez zmeny |
Prestup — ako saga
Čo je saga
Saga je vzor z distribuovaných systémov — rozloží jednu biznis operáciu, ktorá sa dotýka viacerých nezávislých entít, na sekvenciu menších samostatných krokov prepojených spoločným identifikátorom correlation_id. V event-sourced systéme platí pravidlo, že jeden event mení práve jeden agregát. Prestup sa však dotýka dvoch afiliácií naraz (pôvodnej a novej), a preto nemôže byť jeden event. Saga je technická odpoveď, ktorá zachováva jeden logický proces pri troch samostatných udalostiach.
Prestup športovca z klubu A do klubu B sa modeluje ako saga s tromi eventmi prepojenými cez correlation_id:
// 1) TransferRequested na novú (ešte neexistujúcu) afiliáciu
{
"event_type": "TransferRequested",
"aggregate_id": "affil-new-123",
"correlation_id": "transfer-456",
"data": {
"person_id": "person-abc",
"from_organization_id": "club-A",
"to_organization_id": "club-B",
"effective_date": "2026-07-01"
}
}
// 2) AffiliationTerminated na pôvodnej afiliácii
{
"event_type": "AffiliationTerminated",
"aggregate_id": "affil-old-789",
"correlation_id": "transfer-456",
"data": { "reason": "transfer" }
}
// 3) AffiliationRegistered + AffiliationActivated na novej afiliácii
{
"event_type": "AffiliationRegistered",
"aggregate_id": "affil-new-123",
"correlation_id": "transfer-456",
"data": { /* nová afiliácia v klube B */ }
}
Okrem zachovania agregátových hraníc má saga dve praktické výhody. Po prvé, auditovateľnosť — dotaz typu "ukáž mi celý priebeh tohto prestupu" je filter nad event storom podľa jedného correlation_id. Po druhé, kompenzácia — ak sa v poslednom kroku niečo pokazí (napríklad zväz nová afiliáciu odmietne), systém vygeneruje kompenzačné eventy, ktoré obnovia pôvodný stav. Nevracia sa do minulosti rušením udalostí, ale zapíše ďalšie, ktoré pôvodný stav znovu nastolia — všetko ostáva v histórii.
Čo treba vyriešiť od začiatku
Maloletí
Potrebujú legal_guardian vzťah (Person ↔ Person). Súhlasy udeľuje zákonný zástupca do 18 rokov. Pri dovŕšení plnoletosti systém vyzýva na opätovné potvrdenie súhlasov.
Cudzinci
Nemajú rodné číslo. Používajú foreign_id_type + foreign_id_value + foreign_id_country. Deduplikácia cez meno + DOB + typ a hodnota identifikátora.
Zmeny mena
História mien je samostatná entita PERSON_NAME_HISTORY s časovou platnosťou. Aktuálne meno je "posledné platné".
Úmrtia
Notifikácia z RFO → automatické generovanie AffiliationTerminated eventov pre všetky aktívne afiliácie.
Duplicitné identity
Identity Resolution: fuzzy match + manuálny review + merge operácia. Dva person_id sa zlúčia cez PersonsMerged event.
Konflikt záujmov
Rozhodca má aktívnu afiliáciu v jednom z hrajúcich klubov? Systém to deteguje cez dotaz nad current_affiliations. Upozornenie, nie blokovanie.
Nasledujúca kapitola — Číselníky a katalógy — obsahuje zoznam športov, druhov činnosti, typov organizácií a katalóg športovísk.