---
type: BACKEND_SPEC
status: DRAFT
owner: Rulebook Expert
last_update: 2026-04-03
sprint: B-2
---

# B-2 Backend-Übergabe: Regulation-as-Data

> **Zweck:** Vollständige Spezifikation der Lizenz-/Altersmatrix als Regulation-as-Data für das Backend (
> Masterdata-SCS).
> Dieses Dokument ist die verbindliche Übergabe vom 📜 Rulebook Expert an 👷 Backend Developer.
> Nach Fachfreigabe durch das ÖTO-Fachreferat wird der Status von `DRAFT` auf `STABLE` angehoben.

---

## 1. Überblick: Was wird übergeben?

| Artefakt                                                            | Datei / Ort                                                              | Status                      |
|---------------------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------|
| `LizenzKlasseE`-Enum (korrigiert, R4 ergänzt)                       | `core/core-domain/.../Enums.kt`                                          | ✅ Implementiert             |
| Flyway V008 (Turnierklassen + Lizenz-Matrix + Altersklassen Reiter) | `masterdata-service/.../V008__Seed_OETO_2026_Data.sql`                   | ✅ Korrigiert (RD4 entfernt) |
| Flyway V009 (Höhen-Lizenz-Matrix + Mindestalter-Pferd-Matrix)       | `masterdata-service/.../V009__Add_HorseAge_And_LicenseHeight_Matrix.sql` | ✅ Neu angelegt              |
| Validierungsregeln v0.3                                             | `docs/03_Domain/02_Reference/Validierungsregeln.md`                      | DRAFT                       |
| Diese Spezifikation                                                 | `docs/.../B2-Backend-Uebergabe-Regulation-as-Data.md`                    | DRAFT                       |

---

## 2. Enum-Korrekturen (core-domain)

### 2.1 `LizenzKlasseE` — Vollständiger Katalog

```kotlin
enum class LizenzKlasseE {
  LIZENZFREI,  // Keine Lizenz erforderlich (= "ohne Lizenz", CSN-C-NEU)
  R1,          // Reiter-Lizenz Klasse 1
  R2,          // Reiter-Lizenz Klasse 2
  R3,          // Reiter-Lizenz Klasse 3
  R4,          // Reiter-Lizenz Klasse 4  ← NEU ergänzt (war fehlend)
  RD1,         // Dressur-Reiter Klasse 1
  RD2,         // Dressur-Reiter Klasse 2
  RD3,         // Dressur-Reiter Klasse 3
  JN,          // Jugend/Nachwuchs
  JG,          // Junioren
  YR           // Young Rider
}
```

> ⚠️ **Wichtig:** `RD4` existiert **nicht** im ÖTO-Regelwerk. Der bisherige Seed V008 enthielt `RD4` fälschlicherweise —
> wurde in V008 korrigiert (entfernt). Höchste Dressur-Lizenz ist `RD3`.

### 2.2 Schlüssel-Konvention (SSoT)

| Doku-Label                   | Enum-Key (SSoT)  | Hinweis                           |
|------------------------------|------------------|-----------------------------------|
| „ohne Lizenz" / „lizenzfrei" | `LIZENZFREI`     | Nicht `LZF` — Enum ist maßgeblich |
| „mit Lizenz" / „R1"          | `R1`             |                                   |
| „R2 und höher"               | `R2`, `R3`, `R4` | Aufwärts-kompatibel               |

---

## 3. Datenbank-Tabellen (Regulation-as-Data)

### 3.1 Bestehende Tabellen (V005/V008)

| Tabelle          | Zweck                                                                     |
|------------------|---------------------------------------------------------------------------|
| `turnierklasse`  | Klassen-Codes (E, A, L, LM, M, S) je Sparte mit `max_hoehe`               |
| `license_matrix` | Max. Turnierklasse je Lizenz (1 Zeile pro Lizenz × Sparte)                |
| `altersklasse`   | Altersklassen der **Reiter** (Kinder, Jugend, Junioren, YR, AK, Senioren) |

### 3.2 Neue Tabellen (V009)

#### `license_height_matrix`

Granulare Höhen-Lizenz-Zuordnung für Springen (eine Zeile pro Höhenbereich × erlaubte Lizenz).

```sql
-- Abfrage: Welche Lizenzen sind für 110 cm erlaubt?
SELECT lizenz_klasse
FROM license_height_matrix
WHERE sparte = 'SPRINGEN'
  AND hoehe_von_cm <= 110
  AND hoehe_bis_cm >= 110
  AND ist_aktiv = true;
-- Ergebnis: R1, R2, R3, R4
```

#### `horse_min_age_matrix`

Mindestalter des **Pferdes** (Stichtag 1. Jänner) je Sparte + Höhenbereich oder Aufgabenniveau.

```sql
-- Abfrage: Mindestalter für Springen 125 cm?
SELECT min_alter_jahre
FROM horse_min_age_matrix
WHERE sparte = 'SPRINGEN'
  AND hoehe_von_cm <= 125
  AND hoehe_bis_cm >= 125
  AND ist_aktiv = true;
-- Ergebnis: 5
```

---

## 4. Lizenz × Bewerb-Tabellen (DRAFT → Fachfreigabe erforderlich)

### 4.1 Springen (CSN) — Lizenz-Zuordnung nach Hindernishöhe

> Quelle: ÖTO 2026 § 231 | Status: **DRAFT** — Fachfreigabe ausstehend

| Klasse             | Höhe (cm) | Mindest-Lizenz | Zugelassene Lizenzen       | Bemerkung                                                     |
|--------------------|-----------|----------------|----------------------------|---------------------------------------------------------------|
| E0 (Einsteiger)    | 60–95     | LIZENZFREI     | LIZENZFREI, R1, R2, R3, R4 | CSN-C-NEU: Zwangsteilung in Abt. „ohne Lizenz" + „mit Lizenz" |
| A (Anfänger)       | 100–110   | R1             | R1, R2, R3, R4             | Keine LIZENZFREI mehr ab 100 cm                               |
| L (Leicht)         | 115–120   | R1*            | R1, R2, R3, R4             | *Ausschreibung kann R2 vorschreiben                           |
| LM (Leicht-Mittel) | 125–130   | R2             | R2, R3, R4                 |                                                               |
| M (Mittelschwer)   | 135       | R3             | R3, R4                     |                                                               |
| S (Schwer)         | 140–160   | R4             | R4                         |                                                               |

**Aufwärts-Kompatibilität:** Eine höhere Lizenz berechtigt immer zur Teilnahme in niedrigeren Klassen.

### 4.2 Dressur (CDN) — Lizenz-Zuordnung nach Aufgabenniveau

> Quelle: ÖTO 2026 § 103 | Status: **DRAFT** — Fachfreigabe ausstehend

| Klasse             | Aufgabenniveau | Mindest-Lizenz | Zugelassene Lizenzen      |
|--------------------|----------------|----------------|---------------------------|
| Einsteiger         | E              | LIZENZFREI     | LIZENZFREI, RD1, RD2, RD3 |
| A (Anfänger)       | A              | RD1            | RD1, RD2, RD3             |
| L (Leicht)         | L              | RD1            | RD1, RD2, RD3             |
| LM (Leicht-Mittel) | LM             | RD2            | RD2, RD3                  |
| M (Mittelschwer)   | M              | RD2            | RD2, RD3                  |
| S (Schwer)         | S              | RD3            | RD3                       |

---

## 5. Mindestalter Pferd (Regulation-as-Data)

> Stichtagsregel: Alter = `veranstaltungsjahr - geburtsjahr` (Stichtag 1. Jänner)

### 5.1 Springen national (ÖTO § 231)

| Höhe (cm) | Mindestalter Pferd (Jahre) |
|-----------|----------------------------|
| 60–100    | 4                          |
| 101–120   | 5                          |
| 121–999   | 6                          |

### 5.2 Dressur national (ÖTO § 103)

| Aufgabenniveau | Mindestalter Pferd (Jahre) |
|----------------|----------------------------|
| E              | 4                          |
| A              | 4                          |
| L              | 4                          |
| LM             | 5                          |
| M              | 5                          |
| S              | 6                          |

### 5.3 International FEI (GR Art. 136)

| Disziplin | Level           | Mindestalter Pferd     |
|-----------|-----------------|------------------------|
| Springen  | 1*–2*           | 6                      |
| Springen  | 3*–5*           | 7                      |
| Dressur   | CDI Senior      | 7                      |
| Dressur   | CDI Young Horse | 4 (gem. FEI YH-Regeln) |

---

## 6. Serverseitige Validierungslogik (Pseudocode)

Das Backend soll die folgenden Prüfungen serverseitig durchführen (analog zur Frontend-Validierung):

### 6.1 Lizenz-Check (Springen)

```kotlin
fun isLicenseAllowedForHeight(hoeheCm: Int, lizenz: LizenzKlasseE): Boolean {
  // Abfrage license_height_matrix
  val allowed = licenseHeightMatrixRepo.findAllowedLicenses(
    sparte = "SPRINGEN",
    hoehe = hoeheCm
  )
  return lizenz.name in allowed
}
```

### 6.2 Lizenz-Check (Dressur)

```kotlin
fun isLicenseAllowedForLevel(niveau: String, lizenz: LizenzKlasseE): Boolean {
  // Abfrage license_matrix (max_turnierklasse_code Vergleich)
  val maxKlasse = licenseMatrixRepo.findMaxKlasse(sparte = "DRESSUR", lizenz = lizenz.name)
  return klasseOrdnung.indexOf(niveau) <= klasseOrdnung.indexOf(maxKlasse)
}
```

### 6.3 Mindestalter-Pferd-Check

```kotlin
fun isHorseOldEnough(
  geburtsjahr: Int, veranstaltungsjahr: Int,
  sparte: String, hoeheCm: Int?, niveau: String?
): Boolean {
  val alter = veranstaltungsjahr - geburtsjahr  // Stichtag 1. Jänner
  val minAlter = horseMinAgeRepo.findMinAlter(sparte, hoeheCm, niveau)
  return alter >= minAlter
}
```

---

## 7. REST-Endpunkte (Masterdata-SCS) — Empfehlung

| Methode | Pfad                                                              | Beschreibung                                          |
|---------|-------------------------------------------------------------------|-------------------------------------------------------|
| `GET`   | `/api/regulation/license-height-matrix?sparte=SPRINGEN&hoehe=110` | Erlaubte Lizenzen für Höhe                            |
| `GET`   | `/api/regulation/license-matrix?sparte=DRESSUR&lizenz=RD2`        | Max. Klasse für Lizenz                                |
| `GET`   | `/api/regulation/horse-min-age?sparte=SPRINGEN&hoehe=125`         | Mindestalter Pferd                                    |
| `GET`   | `/api/regulation/horse-min-age?sparte=DRESSUR&niveau=LM`          | Mindestalter Pferd (Dressur)                          |
| `GET`   | `/api/fei/resolve/{id}`                                           | FEI Legacy→Numeric Resolver (bereits implementiert ✅) |

---

## 8. Abweichungen Backend ↔ Frontend

| Thema              | Frontend (KMP)                  | Backend (Spring)       | Handlungsbedarf                                             |
|--------------------|---------------------------------|------------------------|-------------------------------------------------------------|
| Lizenz-Keys        | `LZF` (alter Key in Doku)       | `LIZENZFREI` (Enum)    | Doku korrigiert; Frontend-Code prüfen                       |
| R4                 | Vorhanden in `OetoValidators`   | Enum jetzt ergänzt ✅   | Kein weiterer Bedarf                                        |
| RD4                | Nicht vorhanden                 | Seed V008 korrigiert ✅ | Kein weiterer Bedarf                                        |
| Höhen-Matrix       | `OetoValidators.kt` (hardcoded) | V009 als DB-Tabelle    | Backend liest aus DB; Frontend bleibt hardcoded bis Phase 2 |
| Mindestalter Pferd | `OetoValidators.kt` (hardcoded) | V009 als DB-Tabelle    | Wie oben                                                    |

---

## 9. Fachfreigabe-Checkliste (DRAFT → STABLE)

Folgende Punkte müssen vor Anhebung auf `STABLE` durch das ÖTO-Fachreferat bestätigt werden:

- [ ] Lizenz-Zuordnungstabelle Springen (§ 231): Höhenschwellen und Mindestlizenzen korrekt?
- [ ] Lizenz-Zuordnungstabelle Dressur (§ 103): Niveaustufen und Mindestlizenzen korrekt?
- [ ] Mindestalter Pferd Springen (§ 231): Schwellen 4/5/6 Jahre korrekt?
- [ ] Mindestalter Pferd Dressur (§ 103): Schwellen je Niveau korrekt?
- [ ] Aufwärts-Kompatibilität (höhere Lizenz → niedrigere Klasse erlaubt): bestätigt?
- [ ] Paragraphen-Nummern (§ 231, § 103) final verifiziert?
- [ ] Sonderfall CSN-C-NEU Zwangsteilung (LIZENZFREI + R1 in E0): korrekt?

Nach Bestätigung aller Punkte:

1. Status in dieser Datei auf `STABLE` setzen
2. `Validierungsregeln.md` → `status: STABLE`, `version: 1.0`
3. Flyway V009 → Kommentar `Status: DRAFT` entfernen
4. Roadmap B-2 als abgeschlossen markieren

---

## 10. Offene Punkte

| # | Thema                                                           | Verantwortlich           | Priorität  |
|---|-----------------------------------------------------------------|--------------------------|------------|
| 1 | Fachfreigabe Lizenz×Bewerb-Tabellen einholen                    | 📜 Rulebook Expert       | 🔴 Hoch    |
| 2 | Backend-Endpunkte `/api/regulation/*` implementieren            | 👷 Backend               | 🔴 Hoch    |
| 3 | Frontend `LZF`-Key → `LIZENZFREI` prüfen/angleichen             | 🎨 Frontend              | 🟠 Mittel  |
| 4 | `AltersklasseRechner` (C-1) implementieren                      | 👷 Backend + 📜 Rulebook | 🟠 Mittel  |
| 5 | FEI-Mindestalter-Tabellen aus Disziplinregelwerken finalisieren | 📜 Rulebook Expert       | 🟡 Niedrig |
