- Added `PferdReiterEingabe` for horse and rider selection with search functionality and keyboard navigation. - Implemented `NennungenTabelle` to display filtered registrations based on selected horse or rider. - Introduced `NennungsMaske` to combine search, table, and competition views for streamlined user interaction. - Extended types with `Veranstalter` interface and mock data for better context and integration. - Documented ÖTO-compliant tournament structure for frontend reference. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
21 KiB
Architektur-Dokumentation - Lead-Architekt
🏛️ System-Architektur-Übersicht
Architektur-Typ
Single Page Application (SPA) mit Client-Side Routing
Architektur-Pattern
- Component-Based Architecture (React)
- Container/Presentational Pattern
- Data Mode Routing (React Router v7)
🎯 Architektur-Ziele
- Skalierbarkeit: Modulare Komponenten für einfache Erweiterung
- Wartbarkeit: Klare Trennung von UI und Business Logic
- Performance: Code-Splitting, Lazy Loading
- Type-Safety: TypeScript für robuste Typisierung
- Konsistenz: Design System mit Material-UI
🗺️ Anwendungs-Hierarchie
┌─────────────────────────────────────────────────────────────┐
│ App.tsx │
│ (RouterProvider) │
└────────────────────────┬────────────────────────────────────┘
│
├─────────────────────────────────────┐
│ │
┌────▼─────┐ ┌────▼─────┐
│ Login │ │ Dashboard│
│ /login │ │ /admin │
└──────────┘ └────┬─────┘
│
┌────────────────────────────────────┼─────────────────┐
│ │ │
┌────▼─────────────┐ ┌───────────▼──────┐ ┌──────▼─────────┐
│ Veranstalter │ │ TurnierErstellen │ │ TurnierAnsicht │
│ Verwaltung │ │ /veranstaltung/ │ │ /turnier/ │
│ /veranstalter │ │ :id │ │ :veranstaltung │
└──────────────────┘ └─────────┬────────┘ │ Id/:nr │
│ └────────┬───────┘
│ │
┌────────────▼────────┐ │
│ Veranstaltung- │ │
│ Übersicht │ │
│ (Turnier-Karten) │ │
└─────────────────────┘ │
│
┌───────────────────────────────────────────────────────┘
│
┌────────────────┼────────────────┬─────────────────┬──────────────────┐
│ │ │ │ │
┌─────▼────┐ ┌────────▼──────┐ ┌─────▼──────┐ ┌──────▼───────┐ ┌──────▼──────┐
│Stammdaten│ │ Organisation │ │ Bewerbe │ │ Artikel │ │ Abrechnung │
│ Tab │ │ Tab │ │ Tab │ │ Tab │ │ Tab │
└──────────┘ └───────────────┘ └────────────┘ └──────────────┘ └─────────────┘
┌────────────────┬─────────────────┬──────────────────┐
│ │ │ │
┌─────▼────┐ ┌────────▼──────┐ ┌──────▼───────┐ │
│Nennungen │ │ Startlisten │ │Ergebnislisten│ │
│ Tab │ │ Tab │ │ Tab │ │
└──────────┘ └───────────────┘ └──────────────┘ │
🏗️ Komponenten-Architektur
Layer-Struktur
┌─────────────────────────────────────────────┐
│ Presentation Layer │
│ (React Components - UI Only) │
│ - Login.tsx │
│ - Dashboard.tsx │
│ - TurnierAnsicht.tsx │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Container Layer │
│ (Smart Components - State Management) │
│ - StammdatenTab.tsx │
│ - BewerbeTab.tsx │
│ - AbrechnungTab.tsx │
│ - NennungenTab.tsx │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Component Library Layer │
│ (Reusable Components) │
│ - PferdReiterEingabe.tsx │
│ - NennungenTabelle.tsx │
│ - Bewerbsliste.tsx │
│ - VerkaufBuchungen.tsx │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Material-UI Layer │
│ (Design System - MUI Components) │
│ - Box, Paper, Button, TextField, etc. │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Style Layer │
│ (Tailwind CSS v4 + CSS Variables) │
│ - theme.css │
│ - fonts.css │
└─────────────────────────────────────────────┘
🔄 Routing-Architektur
React Router v7 Data Mode
// routes.tsx
const router = createBrowserRouter([
{
path: "/",
element: <Login / >,
},
{
path: "/admin",
element: <Dashboard / >,
},
{
path: "/veranstalter",
element: <VeranstalterVerwaltung / >,
},
{
path: "/veranstaltung/:id",
element: <TurnierErstellen / >,
},
{
path: "/turnier/:veranstaltungId/:nr",
element: <TurnierAnsicht / >,
},
]);
URL-Struktur
/ → Login
/admin → Dashboard (Veranstaltungs-Übersicht)
/veranstalter → Veranstalter-Verwaltung
/veranstaltung/neu → Neue Veranstaltung (mit Veranstalter-Auswahl)
/veranstaltung/:id → Veranstaltung-Übersicht (Turnier-Karten)
/turnier/:veranstaltungId/neu → Neues Turnier → Stammdaten
/turnier/:veranstaltungId/:nr → Turnier-Ansicht (8 Tabs)
💾 Daten-Architektur
Aktueller Stand: Client-Side Mock Data
// Dashboard.tsx
export const veranstaltungenData = [
{
id: number,
name: string,
veranstalter: string,
ort: string,
startDatum: string,
endDatum: string,
status: 'geplant' | 'laufend' | 'abgeschlossen',
turniere: Turnier[]
}
]
Datenmodell-Hierarchie
┌──────────────────┐
│ Veranstalter │
│ (Verein/Club) │
└────────┬─────────┘
│ 1:n
│
┌────────▼─────────┐
│ Veranstaltung │
│ (Event) │
└────────┬─────────┘
│ 1:n
│
┌────────▼─────────┐
│ Turnier │
│ (Competition) │
└────────┬─────────┘
│ 1:n
│
┌────────▼─────────┐
│ Bewerb │
│ (Contest) │
└──────────────────┘
┌──────────────────┐
│ Reiter │
│ (Rider) │
└────────┬─────────┘
│ n:m
│
┌────────▼─────────┐
│ Nennung │
│ (Registration) │
└────────┬─────────┘
│ n:m
│
┌────────▼─────────┐
│ Pferd │
│ (Horse) │
└──────────────────┘
┌──────────────────┐
│ Buchung │
│ (Transaction) │
└──────────────────┘
🎨 Design System-Architektur
Material-UI Theme
// Primärfarbe: Indigo
primary: {
main: '#3F51B5',
light
:
'#7986CB',
dark
:
'#303F9F',
}
// Font-Größen (kompakt für Desktop)
fontSize: {
xs: '10px',
sm
:
'11px',
md
:
'13px',
lg
:
'15px',
}
Tailwind CSS v4 Integration
/* theme.css */
@theme {
--color-primary: #3f51b5;
--color-primary-hover: #303f9f;
--font-size-xs: 10px;
--font-size-sm: 11px;
--font-size-md: 13px;
}
🔐 Authentifizierung-Architektur
Aktuell: Demo-Modus
// Login.tsx
const DEMO_USER = 'admin';
const DEMO_PASSWORD = 'Admin#1234';
Geplant: JWT-basierte Authentifizierung
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────────▶│ Backend │────────▶│ Database │
│ React │ Login │ API │ Verify │ Users │
└────┬─────┘ └────┬─────┘ └──────────┘
│ │
│◀───────────────────┤
│ JWT Token │
│ │
│ API Calls │
│ (with Token) │
├───────────────────▶│
│ │
│◀───────────────────┤
│ Response │
📊 State Management-Architektur
Aktuell: React useState
// Lokaler Component State
const [activeTab, setActiveTab] = useState(0);
const [nennungen, setNennungen] = useState<any[]>([]);
const [selectedPferd, setSelectedPferd] = useState<any>(null);
Empfehlung für Skalierung:
Option 1: React Context API (für mittlere Komplexität)
// TurnierContext.tsx
const TurnierContext = createContext();
export function TurnierProvider({children}) {
const [turnier, setTurnier] = useState();
const [bewerbe, setBewerbe] = useState([]);
return (
<TurnierContext.Provider value = {
{
turnier, bewerbe
}
}>
{
children
}
</TurnierContext.Provider>
)
;
}
Option 2: Zustand (empfohlen für große Apps)
// store/turnierStore.ts
import {create} from 'zustand';
export const useTurnierStore = create((set) => ({
turnier: null,
bewerbe: [],
setTurnier: (turnier) => set({turnier}),
addBewerb: (bewerb) => set((state) => ({
bewerbe: [...state.bewerbe, bewerb]
})),
}));
Option 3: React Query / TanStack Query (für Server State)
// hooks/useTurnier.ts
import {useQuery} from '@tanstack/react-query';
export function useTurnier(id: string) {
return useQuery({
queryKey: ['turnier', id],
queryFn: () => fetchTurnier(id),
});
}
🔌 Backend-Integration (Geplant)
Option 1: REST API
GET /api/veranstaltungen
POST /api/veranstaltungen
GET /api/veranstaltungen/:id
PUT /api/veranstaltungen/:id
DELETE /api/veranstaltungen/:id
GET /api/veranstaltungen/:id/turniere
POST /api/veranstaltungen/:id/turniere
GET /api/turniere/:id
PUT /api/turniere/:id
GET /api/turniere/:id/bewerbe
POST /api/turniere/:id/bewerbe
PUT /api/bewerbe/:id
GET /api/turniere/:id/nennungen
POST /api/nennungen
GET /api/reiter/:id/nennungen
GET /api/pferde/:id/nennungen
GET /api/turniere/:id/buchungen
POST /api/buchungen
GET /api/reiter/:id/buchungen
Option 2: GraphQL (empfohlen für komplexe Queries)
query GetTurnier($id: ID!) {
turnier(id: $id) {
id
name
stammdaten {
znsDaten
oetoTyp
}
bewerbe {
id
name
klasse
}
nennungen {
id
reiter { vorname, nachname }
pferd { name }
bewerb { name }
}
buchungen {
id
text
soll
haben
saldo
}
}
}
Option 3: Supabase (empfohlen für Rapid Development)
// supabase/client.ts
import {createClient} from '@supabase/supabase-js';
export const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_ANON_KEY
);
// Beispiel: Turniere abrufen
const {data, error} = await supabase
.from('turniere')
.select('*, veranstaltung(name), bewerbe(*)')
.eq('id', turnierId);
🚀 Performance-Optimierungen
1. Code-Splitting
// Lazy Loading für Tabs
const BewerbeTab = lazy(() => import('./turnier/BewerbeTab'));
const AbrechnungTab = lazy(() => import('./turnier/AbrechnungTab'));
<Suspense fallback = { < Loading / >
}>
{
activeTab === 2 && <BewerbeTab / >
}
</Suspense>
2. Memoization
// React.memo für teure Components
export const NennungenTabelle = memo(({nennungen, selectedPferd}) => {
// ...
});
// useMemo für teure Berechnungen
const gesamtSaldo = useMemo(() => {
return buchungen.reduce((sum, b) => sum + b.saldo, 0);
}, [buchungen]);
3. Virtualisierung (für große Listen)
import {FixedSizeList} from 'react-window';
<FixedSizeList
height = {600}
itemCount = {bewerbe.length}
itemSize = {50}
>
{({index, style})
=>
(
<div style = {style} > {bewerbe[index].name} < /div>
)
}
</FixedSizeList>
🔒 Sicherheits-Architektur
Geplante Security Features:
- Authentication & Authorization
- JWT Tokens mit Refresh
- Role-Based Access Control (RBAC)
- Session Management
- Input Validation
- Client-Side: React Hook Form + Zod
- Server-Side: Express Validator / Joi
- XSS Protection
- Content Security Policy (CSP)
- Sanitization von User Inputs
- CSRF Protection
- CSRF Tokens für State-Changing Operations
📦 Deployment-Architektur
Empfohlener Stack:
┌─────────────────┐
│ Vercel/ │ → Frontend Hosting (React SPA)
│ Netlify │
└────────┬────────┘
│
│ HTTPS
│
┌────────▼────────┐
│ Backend API │ → Node.js / Express / Nest.js
│ (Railway/ │
│ Render/AWS) │
└────────┬────────┘
│
│
┌────────▼────────┐
│ Database │ → PostgreSQL (Supabase / RDS)
│ (Supabase/ │
│ AWS RDS) │
└─────────────────┘
CI/CD Pipeline:
GitHub Push
│
▼
GitHub Actions
│
├──▶ Lint & Type Check
│
├──▶ Unit Tests
│
├──▶ Build
│
└──▶ Deploy to Vercel
📈 Skalierungs-Strategie
Phase 1: Prototyp (Aktuell)
- Client-Side Mock Data
- React useState
- Keine Backend-Integration
Phase 2: MVP
- Supabase Backend
- REST API Integration
- Basic Authentication
- React Query für Server State
Phase 3: Production
- Dedizierter Backend-Server
- GraphQL API (optional)
- Redis Caching
- WebSocket für Real-Time Updates
- Zustand für Global State
Phase 4: Enterprise
- Microservices-Architektur
- Event-Driven Architecture
- CQRS Pattern
- Message Queue (RabbitMQ / Kafka)
- Multi-Tenant Support
🧪 Testing-Strategie
Unit Tests
// BewerbeTab.test.tsx
import {render, screen} from '@testing-library/react';
import {BewerbeTab} from './BewerbeTab';
test('renders bewerbe table', () => {
render(<BewerbeTab / >);
expect(screen.getByText('Bewerbs-Übersicht')).toBeInTheDocument();
});
Integration Tests
// TurnierAnsicht.integration.test.tsx
test('tab navigation works', async () => {
render(<TurnierAnsicht / >);
const bewerbeTab = screen.getByText('Bewerbe');
await userEvent.click(bewerbeTab);
expect(screen.getByText('Bewerbs-Übersicht')).toBeInTheDocument();
});
E2E Tests (Playwright / Cypress)
// e2e/turnier.spec.ts
test('create new turnier', async ({page}) => {
await page.goto('/admin');
await page.click('text=Neue Veranstaltung');
await page.click('text=RFV Musterstadt');
await page.fill('[name="name"]', 'Frühjahrsturnier 2026');
await page.click('text=Speichern');
await expect(page.locator('text=Frühjahrsturnier 2026')).toBeVisible();
});
📚 Architektur-Entscheidungen (ADRs)
ADR-001: React Router Data Mode
Status: Angenommen
Kontext: Benötigen Client-Side Routing mit mehreren verschachtelten Routen
Entscheidung: React Router v7 mit Data Mode Pattern
Konsequenzen: Einfache Navigation, aber keine SSR
ADR-002: Material-UI als Design System
Status: Angenommen
Kontext: Benötigen professionelles Design System für Desktop-App
Entscheidung: Material-UI v6 (Material Design 3)
Konsequenzen: Konsistentes Design, aber größere Bundle-Size
ADR-003: Tailwind CSS für Custom Styles
Status: Angenommen
Kontext: Benötigen Utility-First CSS für schnelles Styling
Entscheidung: Tailwind CSS v4 zusätzlich zu MUI
Konsequenzen: Flexibles Styling, aber zwei CSS-Systeme
ADR-004: TypeScript für Type-Safety
Status: Angenommen
Kontext: Komplexe Datenstrukturen benötigen Type-Safety
Entscheidung: TypeScript statt JavaScript
Konsequenzen: Bessere DX, aber längere Compile-Zeit
🔮 Zukunfts-Roadmap
Q2 2026
- Supabase Backend-Integration
- REST API für alle CRUD-Operationen
- PDF-Export für Startlisten/Rechnungen
- Excel-Export
Q3 2026
- Real-Time Collaboration (WebSockets)
- Mobile-Responsive Layout
- Offline-Modus (PWA)
- Multi-Language Support (i18n)
Q4 2026
- GraphQL API
- Advanced Analytics Dashboard
- Email-Benachrichtigungen
- Zahlungs-Gateway Integration
Dokumentiert von: Lead-Architekt
Version: 1.0
Datum: 2026-03-24