meldestelle/domains/horses/README-HORSES.md
StefanMo 9ea2b74a81
Merge pull request #18
* MP-19 Refactoring: Einführung der "Registry" & "Masterdata" Trennung …

* MP-19 Refactoring: Frontend Tabula Rasa

* MP-19 Refactoring: Frontend Tabula Rasa

* refactoring:

* MP-20 fix(docker/clients): include `:domains` module in web/desktop b…

* MP-20 fix(web-app build): resolve JS compile error and add dev/prod b…

* MP-20 fix(web-app): remove vendor.js reference and harden JS bootstra…

* MP-20 fixing: clients

* MP-20 fixing: clients
2025-11-30 14:13:12 +01:00

16 KiB

Horses Module

Überblick

Das Horses-Modul ist eine umfassende Lösung zur Verwaltung von Pferden für Pferdesportorganisationen. Es implementiert eine saubere Architektur mit Domain-Driven Design und bietet vollständige CRUD-Operationen sowie erweiterte Geschäftslogik für die Pferderegistrierung und -verwaltung.

Funktionalität

Verwaltete Entität

Pferd (DomPferd)

  • Grundinformationen: Name, Geschlecht, Geburtsdatum, Rasse, Farbe
  • Besitz und Verantwortung: Besitzer-ID, verantwortliche Person
  • Zuchtinformationen: Züchtername, Zuchtbuchnummer
  • Identifikationsnummern: Lebensnummer, Chipnummer, Passnummer, OEPS-Nummer, FEI-Nummer
  • Abstammung: Vater, Mutter, Muttervater
  • Körperliche Merkmale: Stockmaß (Höhe in cm)
  • Status und Verwaltung: Aktivitätsstatus, Bemerkungen, Datenquelle
  • Audit-Felder: Erstellungs- und Aktualisierungszeitstempel

Geschäftsoperationen

Das Modul bietet 25+ spezialisierte Repository-Operationen:

Basis-CRUD-Operationen

  • findById(id) - Pferd nach UUID suchen
  • save(horse) - Pferd speichern (erstellen/aktualisieren)
  • delete(id) - Pferd löschen

Such-Operationen nach Identifikationsnummern

  • findByLebensnummer(lebensnummer) - Nach Lebensnummer suchen
  • findByChipNummer(chipNummer) - Nach Chipnummer suchen
  • findByPassNummer(passNummer) - Nach Passnummer suchen
  • findByOepsNummer(oepsNummer) - Nach OEPS-Nummer suchen
  • findByFeiNummer(feiNummer) - Nach FEI-Nummer suchen

Such-Operationen nach Eigenschaften

  • findByName(searchTerm, limit) - Nach Namen suchen (Teilübereinstimmung)
  • findByOwnerId(ownerId, activeOnly) - Pferde eines Besitzers
  • findByResponsiblePersonId(personId, activeOnly) - Pferde einer verantwortlichen Person
  • findByGeschlecht(geschlecht, activeOnly, limit) - Nach Geschlecht filtern
  • findByRasse(rasse, activeOnly, limit) - Nach Rasse filtern

Datumsbasierte Abfragen

  • findByBirthYear(birthYear, activeOnly) - Pferde nach Geburtsjahr
  • findByBirthYearRange(fromYear, toYear, activeOnly) - Pferde nach Geburtsjahr-Bereich

Registrierungs-Abfragen

  • findAllActive(limit) - Alle aktiven Pferde
  • findOepsRegistered(activeOnly) - OEPS-registrierte Pferde
  • findFeiRegistered(activeOnly) - FEI-registrierte Pferde

Validierungs-Operationen

  • existsByLebensnummer(lebensnummer) - Prüfung auf doppelte Lebensnummer
  • existsByChipNummer(chipNummer) - Prüfung auf doppelte Chipnummer
  • existsByPassNummer(passNummer) - Prüfung auf doppelte Passnummer
  • existsByOepsNummer(oepsNummer) - Prüfung auf doppelte OEPS-Nummer
  • existsByFeiNummer(feiNummer) - Prüfung auf doppelte FEI-Nummer

Zähl-Operationen

  • countActive() - Anzahl aktiver Pferde
  • countByOwnerId(ownerId, activeOnly) - Anzahl Pferde pro Besitzer
  • countOepsRegistered(activeOnly) - Anzahl OEPS-registrierter Pferde NEU
  • countFeiRegistered(activeOnly) - Anzahl FEI-registrierter Pferde NEU

Architektur

Das Modul folgt der Clean Architecture mit klarer Trennung der Verantwortlichkeiten:

horses/
├── horses-domain/               # Domain Layer
│   ├── model/                   # Domain Models
│   │   └── DomPferd.kt         # Pferd-Entität mit Geschäftslogik
│   └── repository/             # Repository Interfaces
│       └── HorseRepository.kt  # 25+ Geschäftsoperationen
├── horses-application/          # Application Layer
│   └── usecase/                # Use Cases
│       ├── CreateHorseUseCase.kt
│       ├── GetHorseUseCase.kt
│       ├── UpdateHorseUseCase.kt
│       └── DeleteHorseUseCase.kt
├── horses-infrastructure/       # Infrastructure Layer
│   └── persistence/            # Database Implementation
│       ├── HorseRepositoryImpl.kt
│       └── HorseTable.kt
├── horses-api/                 # API Layer
│   └── rest/                   # REST Controllers
│       └── HorseController.kt
└── horses-service/             # Service Layer
    ├── HorsesServiceApplication.kt
    └── test/                   # Integration Tests
        └── HorseServiceIntegrationTest.kt

Domain Layer

  • 1 Domain Model mit reichhaltiger Geschäftslogik
  • 1 Repository Interface mit 25+ Geschäftsoperationen
  • Geschäftsregeln für Pferderegistrierung und -validierung
  • Keine Abhängigkeiten zu anderen Layern

Application Layer

  • Use Cases für CRUD-Operationen
  • Orchestrierung von Domain-Services
  • Anwendungslogik ohne UI-Abhängigkeiten

Infrastructure Layer

  • Datenbankzugriff mit Exposed ORM
  • Repository-Implementierung mit PostgreSQL
  • Datenbankschema und Migrationen

API Layer

  • REST-Controller für HTTP-Endpunkte

🚀 Aktuelle Optimierungen (2025-07-25)

Das Horses-Modul wurde kürzlich analysiert, vervollständigt und optimiert. Folgende Verbesserungen wurden implementiert:

Neue Funktionalitäten

Erweiterte Such-Endpunkte

Neue REST-Endpunkte für vollständige Identifikationsnummer-Suche:

  • GET /api/horses/search/passport/{nummer} - Suche nach Passnummer
  • GET /api/horses/search/oeps/{nummer} - Suche nach OEPS-Nummer
  • GET /api/horses/search/fei/{nummer} - Suche nach FEI-Nummer

Optimierte Statistik-Operationen

  • Neue effiziente Zähl-Methoden für OEPS und FEI registrierte Pferde
  • Performance-Verbesserung von O(n) auf O(1) Komplexität für Statistiken
  • Datenbankoptimierte COUNT-Abfragen statt Laden aller Datensätze

Performance-Optimierungen

Datenbankeffizienz

  • Vorher: Statistik-Endpunkt lud alle Pferde und verwendete .size
  • Nachher: Effiziente COUNT-Abfragen direkt in der Datenbank
  • Auswirkung: Drastische Reduzierung der Speichernutzung und Antwortzeiten

Architektur-Konsistenz

  • Alle API-Endpunkte verwenden jetzt konsistent die Use-Case-Schicht
  • Eliminierung direkter Repository-Aufrufe in der API-Schicht
  • Saubere Trennung der Architektur-Schichten

🏗️ Architektur-Verbesserungen

Clean Architecture Compliance

  • Konsistente Schichtung: Alle Endpunkte folgen dem Use-Case-Pattern
  • Fehlerbehandlung: Einheitliche Fehlerantworten über alle Endpunkte
  • Validierung: Umfassende Eingabevalidierung mit geteilten Utilities
  • HTTP-Standards: Korrekte Status-Codes und REST-Konventionen

Code-Qualität

  • Verbesserte Lesbarkeit und Wartbarkeit
  • Konsistente Namenskonventionen
  • Umfassende Dokumentation aller neuen Funktionen

📊 Qualitätsmetriken

Vor der Optimierung

  • Fehlende Such-Endpunkte für 3 Identifikationstypen
  • Ineffiziente Statistik-Abfragen (O(n) Komplexität)
  • Inkonsistente Architektur (einige Endpunkte umgingen Use Cases)
  • Performance-Probleme bei großen Datensätzen

Nach der Optimierung

  • Vollständige API-Abdeckung für alle Identifikationstypen
  • Effiziente Statistik-Abfragen (O(1) Komplexität)
  • Konsistente Clean Architecture durchgehend
  • Optimierte Performance für alle Operationen

🔮 Zukünftige Empfehlungen

Caching-Schicht

  • Implementierung einer Caching-Schicht für häufig abgerufene Daten
  • Individuelle Pferde-Lookups mit angemessener TTL
  • Statistiken und Zählungen mit Cache-Invalidierung

Async-Operationen

  • Asynchrone Verarbeitung für Batch-Operationen
  • Komplexe Such-Abfragen mit Async-Pattern
  • Statistik-Berechnungen im Hintergrund

Monitoring und Logging

  • Umfassendes Monitoring für API-Antwortzeiten
  • Datenbank-Query-Performance-Überwachung
  • Fehlerrate-Tracking und -Analyse
  • DTO-Mapping zwischen Domain und API
  • Validierung und Fehlerbehandlung

Service Layer

  • Spring Boot Anwendung
  • Dependency Injection Konfiguration
  • Integrationstests

Domain Model Details

DomPferd-Entität

data class DomPferd(
    val pferdId: Uuid,

    // Grundinformationen
    var pferdeName: String,
    var geschlecht: PferdeGeschlechtE,
    var geburtsdatum: LocalDate? = null,
    var rasse: String? = null,
    var farbe: String? = null,

    // Besitz und Verantwortung
    var besitzerId: Uuid? = null,
    var verantwortlichePersonId: Uuid? = null,

    // Zuchtinformationen
    var zuechterName: String? = null,
    var zuchtbuchNummer: String? = null,

    // Identifikationsnummern
    var lebensnummer: String? = null,
    var chipNummer: String? = null,
    var passNummer: String? = null,
    var oepsNummer: String? = null,
    var feiNummer: String? = null,

    // Abstammung
    var vaterName: String? = null,
    var mutterName: String? = null,
    var mutterVaterName: String? = null,

    // Körperliche Merkmale
    var stockmass: Int? = null, // Höhe in cm

    // Status und Verwaltung
    var istAktiv: Boolean = true,
    var bemerkungen: String? = null,
    var datenQuelle: DatenQuelleE = DatenQuelleE.MANUELL,

    // Audit-Felder
    val createdAt: Instant,
    var updatedAt: Instant
)

Geschäftslogik-Methoden

  • getDisplayName() - Anzeigename mit Geburtsjahr
  • hasCompleteIdentification() - Prüfung auf vollständige Identifikation
  • isOepsRegistered() - OEPS-Registrierungsstatus
  • isFeiRegistered() - FEI-Registrierungsstatus
  • getAge() - Altersberechnung in Jahren
  • validateForRegistration() - Validierung für Registrierung
  • withUpdatedTimestamp() - Kopie mit aktualisiertem Zeitstempel

Enumerationen

PferdeGeschlechtE

  • HENGST - Hengst (männlich, nicht kastriert)
  • STUTE - Stute (weiblich)
  • WALLACH - Wallach (männlich, kastriert)

DatenQuelleE

  • MANUELL - Manuelle Eingabe
  • IMPORT - Datenimport
  • SYNCHRONISATION - Synchronisation mit externen Systemen

Repository-Operationen

Erweiterte Such-Features

// Pferde nach Identifikationsnummer suchen
val horse = horseRepository.findByLebensnummer("AT123456789")
val chipHorse = horseRepository.findByChipNummer("982000123456789")

// Pferde eines Besitzers finden
val ownerHorses = horseRepository.findByOwnerId(ownerId, activeOnly = true)

// Pferde nach Eigenschaften filtern
val stallions = horseRepository.findByGeschlecht(PferdeGeschlechtE.HENGST)
val warmbloods = horseRepository.findByRasse("Warmblut", activeOnly = true)

// Pferde nach Geburtsjahr suchen
val youngHorses = horseRepository.findByBirthYearRange(2020, 2024)

Registrierungs-Abfragen

// OEPS-registrierte Pferde finden
val oepsHorses = horseRepository.findOepsRegistered(activeOnly = true)

// FEI-registrierte Pferde finden
val feiHorses = horseRepository.findFeiRegistered(activeOnly = true)

// Alle aktiven Pferde
val activeHorses = horseRepository.findAllActive(limit = 1000)

Validierung und Duplikatsprüfung

// Prüfung auf doppelte Identifikationsnummern
val lebensnummerExists = horseRepository.existsByLebensnummer("AT123456789")
val chipExists = horseRepository.existsByChipNummer("982000123456789")
val oepsExists = horseRepository.existsByOepsNummer("AUT12345")

Use Cases

CreateHorseUseCase

Erstellt ein neues Pferd mit Validierung und Duplikatsprüfung.

class CreateHorseUseCase(
    private val horseRepository: HorseRepository
) {
    suspend fun execute(horse: DomPferd): DomPferd {
        // Validierung
        val errors = horse.validateForRegistration()
        if (errors.isNotEmpty()) {
            throw ValidationException(errors)
        }

        // Duplikatsprüfung
        horse.lebensnummer?.let { nummer ->
            if (horseRepository.existsByLebensnummer(nummer)) {
                throw DuplicateException("Lebensnummer bereits vorhanden")
            }
        }

        return horseRepository.save(horse)
    }
}

GetHorseUseCase

Ruft Pferdeinformationen ab mit verschiedenen Suchkriterien.

UpdateHorseUseCase

Aktualisiert Pferdeinformationen mit Validierung.

DeleteHorseUseCase

Löscht ein Pferd (soft delete durch Deaktivierung).

API-Endpunkte

Das Horses-Modul stellt REST-Endpunkte über den HorseController bereit:

  • GET /api/horses - Alle aktiven Pferde abrufen
  • GET /api/horses/{id} - Pferd nach ID abrufen
  • GET /api/horses/search?name={name} - Pferde nach Namen suchen
  • GET /api/horses/owner/{ownerId} - Pferde eines Besitzers
  • GET /api/horses/identification/{number} - Pferd nach Identifikationsnummer
  • GET /api/horses/oeps-registered - OEPS-registrierte Pferde
  • GET /api/horses/fei-registered - FEI-registrierte Pferde
  • POST /api/horses - Neues Pferd erstellen
  • PUT /api/horses/{id} - Pferd aktualisieren
  • DELETE /api/horses/{id} - Pferd löschen

Konfiguration

Datenbankschema

Das Modul verwendet eine horses-Tabelle mit folgenden Spalten:

  • pferd_id (UUID, Primary Key)
  • pferde_name (Required)
  • geschlecht (Enum: HENGST, STUTE, WALLACH)
  • geburtsdatum, rasse, farbe (Optional)
  • besitzer_id, verantwortliche_person_id (UUID, Foreign Keys)
  • zuechter_name, zuchtbuch_nummer (Optional)
  • lebensnummer, chip_nummer, pass_nummer (Unique, Optional)
  • oeps_nummer, fei_nummer (Unique, Optional)
  • vater_name, mutter_name, mutter_vater_name (Optional)
  • stockmass (Integer, Optional)
  • ist_aktiv (Boolean)
  • bemerkungen (Text, Optional)
  • daten_quelle (Enum)
  • created_at, updated_at (Timestamps)

Service-Konfiguration

# application.yml
horses:
  service:
    name: horses-service
    port: 8083
  database:
    url: jdbc:postgresql://localhost:5432/meldestelle
    table: horses
  validation:
    require-identification: true
    allow-duplicate-names: false

Tests

Integration Tests

Das Modul enthält umfassende Integrationstests:

@Test
fun `should create horse with valid data`() {
    // Test für Pferdeerstellung
}

@Test
fun `should find horses by owner`() {
    // Test für Besitzer-basierte Suche
}

@Test
fun `should validate unique identification numbers`() {
    // Test für Eindeutigkeit der Identifikationsnummern
}

Test-Datenbank

Verwendet H2 In-Memory-Datenbank für Tests mit automatischem Schema-Setup.

Deployment

Docker

FROM openjdk:21-jre-slim
COPY horses-service.jar app.jar
EXPOSE 8083
ENTRYPOINT ["java", "-jar", "/app.jar"]

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: horses-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: horses-service
  template:
    spec:
      containers:
      - name: horses-service
        image: meldestelle/horses-service:latest
        ports:
        - containerPort: 8083

Monitoring

Metriken

  • Anzahl aktiver Pferde
  • Anzahl registrierter Pferde (OEPS/FEI)
  • API-Response-Zeiten
  • Datenbankverbindungs-Pool
  • Validierungsfehler-Rate

Health Checks

  • Datenbankverbindung
  • Service-Verfügbarkeit
  • Speicherverbrauch
  • Externe System-Verbindungen

Entwicklung

Lokale Entwicklung

# Service starten
./gradlew :horses:horses-service:bootRun

# Tests ausführen
./gradlew :horses:test

# Integration Tests
./gradlew :horses:horses-service:test

Code-Qualität

  • Kotlin Coding Standards
  • 100% Test Coverage für Domain Layer
  • Integration Tests für alle Use Cases
  • API-Dokumentation mit OpenAPI

Compliance und Standards

OEPS-Integration

  • Unterstützung für OEPS-Nummern
  • Validierung nach OEPS-Standards
  • Synchronisation mit OEPS-Datenbank

FEI-Integration

  • Unterstützung für FEI-Nummern
  • Internationale Registrierungsstandards
  • Compliance mit FEI-Regularien

Datenschutz

  • DSGVO-konforme Datenhaltung
  • Anonymisierung von Testdaten
  • Audit-Trail für alle Änderungen

Zukünftige Erweiterungen

  1. Gesundheitsdaten - Veterinärmedizinische Aufzeichnungen
  2. Leistungsdaten - Turnierergebnisse und Bewertungen
  3. Versicherungsdaten - Integration mit Versicherungssystemen
  4. Foto-Management - Bildverwaltung für Pferde
  5. Stammbaum-Visualisierung - Grafische Darstellung der Abstammung
  6. Import/Export - Datenimport aus externen Systemen
  7. Mobile App - Mobile Anwendung für Pferdebesitzer
  8. QR-Code-Integration - QR-Codes für schnelle Identifikation

Letzte Aktualisierung: 25. Juli 2025

Für weitere Informationen zur Gesamtarchitektur siehe README.md.