Files
meldestelle/docs/06_Frontend/FIGMA/Vision_03/docs/ARCHITECTURE.md
T
stefan 7702574904 feat(ui): introduce PferdReiterEingabe, NennungenTabelle, and NennungsMaske components
- 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>
2026-03-24 13:49:21 +01:00

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

  1. Skalierbarkeit: Modulare Komponenten für einfache Erweiterung
  2. Wartbarkeit: Klare Trennung von UI und Business Logic
  3. Performance: Code-Splitting, Lazy Loading
  4. Type-Safety: TypeScript für robuste Typisierung
  5. 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:

  1. Authentication & Authorization
  • JWT Tokens mit Refresh
  • Role-Based Access Control (RBAC)
  • Session Management
  1. Input Validation
  • Client-Side: React Hook Form + Zod
  • Server-Side: Express Validator / Joi
  1. XSS Protection
  • Content Security Policy (CSP)
  • Sanitization von User Inputs
  1. 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