togethere.cloud/mds/DISCIPLINE_SETTINGS_ARCHITECTURE.md

468 lines
18 KiB
Markdown

# 🏗️ Architektura Systemu Ustawień Dyscyplin
## 📐 Diagram Przepływu
```
┌─────────────────────────────────────────────────────────────────┐
│ UŻYTKOWNIK │
│ (Admin lub Gracz) │
└──────────────────────────┬──────────────────────────────────────┘
┌────────────┴────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ PANEL ADMINA │ │ GRA KLIENCKA │
│ (UI do edycji) │ │ (Startup gry) │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ │
GET/POST │ │ GET
/admin.. │ │ /api/..
│ │
└────────────┬────────────┘
┌──────────────────────────┐
│ KONTROLER (index.php) │
│ - Routing GET/POST │
│ - Auth check (admin) │
│ - JSON handling │
└────────────┬─────────────┘
┌──────────────────────────┐
│ SERVICE (Biznesowa logika)
│ - Walidacja │
│ - Transformacja │
│ - Versioning │
│ - Snapshot │
└────────────┬─────────────┘
┌──────────────────────────┐
│ MODEL (DB Access) │
│ - CRUD │
│ - Walidacja pierwotna │
│ - Transakcje │
└────────────┬─────────────┘
┌──────────────────────────┐
│ BAZA DANYCH (MySQL) │
│ settings_disciplines │
└──────────────────────────┘
```
---
## 🔄 Scenariusz 1: Admin zmienia ustawienia
```
1. Admin otwiera panel
GET /administration/disciplines/ping-pong/
2. Service pobiera bieżące ustawienia
$service->getSettingsForAPI('ping-pong')
3. UI wyświetla form z obecnymi wartościami
4. Admin zmienia pointsToWin (11 → 21) i zapisuje
POST /administration/disciplines/ping-pong/settings
5. Kontroler odbiera JSON:
{
"rules": { "pointsToWin": 21, ... },
"customization": { ... }
}
6. Service waliduje
✅ pointsToWin: 21 (OK, od 1 do 100)
✅ settingsVersion zwiększ (1 → 2)
✅ Nieparzyste (OK)
7. Model UPDATE w BD
UPDATE settings_disciplines SET
pointsToWin = 21,
settingsVersion = 2,
updated_at = NOW(),
updated_by = admin_id
WHERE discipline = 'ping-pong'
8. Model zwraca: { settingsVersion: 2, ... }
9. Service formatuje odpowiedź
10. Kontroler zwraca JSON z status 200
{
"success": true,
"message": "Settings updated",
"data": { settingsVersion: 2, ... }
}
11. UI odświeża panel - pokazuje wersję 2
```
---
## 🎮 Scenariusz 2: Gra pobiera ustawienia
```
1. Gra startuje w przeglądarce
2. JavaScript: loadGameSettings()
3. Pobiera snapshot
GET /api/discipline-settings.php?
discipline=ping-pong&snapshot=true
4. Kontroler (discipline-settings.php):
- Nie sprawdza auth (publiczny endpoint)
- Query: discipline=ping-pong
- Model: getSnapshot('ping-pong')
5. Model zwraca snapshot
{
"discipline": "ping-pong",
"settingsVersion": 2,
"rules": {
"pointsToWin": 21,
...
},
"snapshotTimestamp": "2026-01-28 12:35:10"
}
6. Kontroler zwraca JSON
{
"success": true,
"snapshot": { ... }
}
7. JavaScript inicjalizuje grę z snapshot'em
new PingPongGame({
pointsToWin: 21,
setsToWin: 3,
settingsVersion: 2
})
8. Gracz gra i uzyskuje wynik
9. JavaScript wysyła wynik na backend
POST /api/matches_sync.php
{
"team1_score": 3,
"team2_score": 2,
"settingsVersion": 2,
"snapshotTimestamp": "2026-01-28 12:35:10"
}
10. Backend zapisuje mecz z settingsVersion
Dzięki temu wiadomo, jakie reguły obowiązywały
```
---
## 📊 Tabela Ustawień vs Mechanika Gry
```
Tabela: settings_disciplines
┌─────────────────┬──────────────────────────────────┐
│ Kolumna │ Wpływ │
├─────────────────┼──────────────────────────────────┤
│ pointsToWin │ ✅ Logika gry │
│ setsToWin │ ✅ Logika gry │
│ serveRotation │ ✅ Logika gry │
│ specialRules │ ✅ Logika gry (informacyjnie) │
│ customization │ ❌ Tylko UI (bez wpływu) │
│ settingsVersion │ ⚠️ Dla tracking'u │
└─────────────────┴──────────────────────────────────┘
✅ = Wpływa na wynik meczu
❌ = Tylko wizualne
⚠️ = Metadata
```
---
## 🔒 Warstwy Bezpieczeństwa
```
┌─────────────────────────────────────────────────┐
│ ENDPOINT ADMINISTRACYJNY │
│ /administration/disciplines/{disc}/settings │
├─────────────────────────────────────────────────┤
│ 1. Session check │
│ ✓ Czy zalogowany? │
├─────────────────────────────────────────────────┤
│ 2. Role check │
│ ✓ Czy admin? │
├─────────────────────────────────────────────────┤
│ 3. Input validation (Service) │
│ ✓ Typy, zakresy, wymagane pola │
├─────────────────────────────────────────────────┤
│ 4. Business logic validation (Service) │
│ ✓ Nieparzyste liczby │
│ ✓ Logika biznesowa │
├─────────────────────────────────────────────────┤
│ 5. Database operations (Model) │
│ ✓ Prepared statements (SQL injection) │
│ ✓ Transakcje (ACID) │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ ENDPOINT PUBLICZNY (API) │
│ /api/discipline-settings.php │
├─────────────────────────────────────────────────┤
│ 1. Method check │
│ ✓ Tylko GET (bez POST) │
├─────────────────────────────────────────────────┤
│ 2. Discipline validation │
│ ✓ Tylko znane dyscypliny │
├─────────────────────────────────────────────────┤
│ 3. Read-only (brak możliwości zmian) │
│ ✓ Model tylko SELECT │
├─────────────────────────────────────────────────┤
│ 4. Database queries │
│ ✓ Prepared statements │
└─────────────────────────────────────────────────┘
```
---
## 🗂️ Warianty Danych
```
┌──────────────────────┬──────────┬──────────┐
│ Variant │ Gdzie │ Kiedy │
├──────────────────────┼──────────┼──────────┤
│ Defaults (hardcoded) │ Code │ Zawsze │
│ DB Snapshot 1 │ Database │ Przy DB │
│ DB Snapshot 2 │ Database │ Po zmian │
│ Match Snapshot │ Match │ Start │
└──────────────────────┴──────────┴──────────┘
Defaults → Database (first run)
Current Version (v1)
Admin zmienia
New Version (v2)
Każdy nowy mecz → Snapshot v2
Stare mecze (v1) → Zachowują v1
```
---
## 🔁 Cykl Życia Ustawienia
```
CREATE (v1)
├─ Domyślne wartości z kodu
├─ Zapisane w BD
MODIFY (v1 → v2)
├─ Admin zmienia pointsToWin
├─ Service: walidacja
├─ Model: settingsVersion++
├─ UPDATE w BD
SNAPSHOT
├─ Gra: pobiera snapshot (v2)
├─ Match: zapisany z version = 2
MODIFY (v2 → v3)
├─ Admin zmienia kolory
├─ Service: walidacja
├─ Model: settingsVersion++
SNAPSHOT
├─ Nowe gry: version 3
├─ Stare gry: version 1 i 2 nie zmienią się
ANALYTICS
├─ Porównanie: średnia czasu meczu v1 vs v2 vs v3
├─ Wnioski: która wersja miała najlepsze wyniki
```
---
## 📱 Komponenty
### 1. Model (DisciplineSettingsModel)
```php
- getSettings($discipline)
- getSettingsByVersion($discipline, $version)
- updateSettings($discipline, $settings, $userId)
- getSnapshot($discipline, $version)
- ensureTableExists()
- getDefaults($discipline)
```
### 2. Service (DisciplineSettingsService)
```php
- getSettingsForAPI($discipline)
- validateAndUpdate($discipline, $input, $userId)
- getMatchSnapshot($discipline, $version)
- resetToDefaults($discipline, $userId)
- compareVersions($old, $new)
```
### 3. Controller (index.php)
```php
handleGetSettings($service, $discipline)
handlePostSettings($service, $discipline)
- Routing
- Auth
- JSON parsing
- Error handling
```
### 4. Public API (discipline-settings.php)
```php
- GET only
- No auth required
- Zwraca snapshot
- Dla gry kliencka
```
---
## 🧮 Walidacja - Etapy
```
┌─────────────────────────────────────────┐
│ Input: POST JSON │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ JSON Parse (Controller) │
│ Czy to poprawny JSON? │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ Type Check (Service) │
│ Czy rules to array? Czy customization │
│ to object? │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ Range Check (Service) │
│ pointsToWin: 1-100? │
│ setsToWin: 1-100? │
│ serveRotation: 1-50? │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ Business Logic (Service) │
│ Nieparzyste liczby? │
│ Consistency check? │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ DB Level (Model) │
│ Constraints (unique, NOT NULL) │
│ Prepared statements │
└────────────┬────────────────────────────┘
┌─────────────────────────────────────────┐
│ ✅ Sukces lub ❌ Błąd │
│ Z powrotem do controllera │
└─────────────────────────────────────────┘
```
---
## 🚦 HTTP Status Codes
```
┌─────┬─────────────────────────────────────────┐
│ 200 │ OK - Operacja udana │
├─────┼─────────────────────────────────────────┤
│ 400 │ Bad Request - Błąd walidacji │
├─────┼─────────────────────────────────────────┤
│ 401 │ Unauthorized - Nie zalogowany │
├─────┼─────────────────────────────────────────┤
│ 403 │ Forbidden - Brak roli admin │
├─────┼─────────────────────────────────────────┤
│ 405 │ Method Not Allowed - Typ request │
├─────┼─────────────────────────────────────────┤
│ 500 │ Server Error - Błąd bazy / inne │
└─────┴─────────────────────────────────────────┘
```
---
## 🔄 Versioning Strategy
```
v1 (Initial)
├─ pointsToWin: 11
├─ setsToWin: 3
└─ serveRotation: 2
v2 (After change)
├─ pointsToWin: 21 ← ZMIANA
├─ setsToWin: 3
└─ serveRotation: 2
v3 (After another change)
├─ pointsToWin: 21
├─ setsToWin: 3
└─ serveRotation: 3 ← ZMIANA
Każda zmiana = nowa wersja
Snapshot zawsze ma konkretną wersję
Stare gry zawsze "widzą" swoją wersję
```
---
## 📈 Możliwości Rozszerzenia
```
Teraz:
settings_disciplines (bieżące)
Przyszłość:
settings_disciplines_history
├─ Pełna historia zmian
├─ Kto zmienił i kiedy
├─ Co się zmieniło
└─ Rollback
settings_scheduled_changes
├─ Zmiana planowana na czasę
├─ Notyfikacja dla użytkowników
settings_ab_tests
├─ Grupa A: wersja 1
├─ Grupa B: wersja 2
└─ Analytics
```
---
## 🎯 Kluczowe Założenia
1. **Immutability** - snapshot po starcie nie zmienia się
2. **Versioning** - każda zmiana = nowa wersja
3. **Transparency** - każdy mecz wie jakie reguły obowiązywały
4. **Backward Compatibility** - stare gry nie wpływane zmianami
5. **Auditability** - każda zmiana jest zalogowana
6. **Extensibility** - łatwo dodać nową dyscyplinę