wiki/d-guides/coding-conventions.md
2026-04-11 19:45:08 +02:00

8.6 KiB

Coding-Konventionen

Naming

  • Alle internen Funktionen beginnen mit _ Prefix (nicht exportierbar)
  • camelCase fuer Variablen und Funktionsnamen (kein snake_case)
  • PascalCase fuer Klassen und Pydantic-Models
  • Dateien: camelCase fuer Module (z.B. mainServiceAi.py, routeBilling.py)

Frontend (React/TypeScript)

  • Keine Browser-Dialoge (alert, confirm, prompt) -- stattdessen useConfirm() / usePrompt() Hooks
  • CSS Modules fuer Styling
  • Hooks-Pattern fuer State und API-Zugriffe (useApiRequest, useBilling, etc.)
  • Fehler propagieren -- keine stillen Fallbacks bei kritischen Pfaden

i18n-Pflicht: t() fuer alle UI-Texte

Jeder sichtbare Text im UI (Labels, Buttons, Placeholders, Tooltips, Fehlermeldungen) muss mit t() getaggt werden. Hardcodierte deutsche Strings im JSX sind nicht erlaubt.

import { useLanguage } from '../../providers/language/LanguageContext';

const { t } = useLanguage();

// Einfacher Text
t('Speichern')

// Mit Variablen-Interpolation
t('{count} Eintraege gefunden', { count: String(total) })

// Gleicher Text, anderer Kontext → Klammer als Kontext-Hinweis
t('Offen (Status)')   // vs.  t('Offen (Zustand)')
  • Key = deutscher Klartext (kein Dot-Notation-Schema)
  • Kein Plural-Framework -- separate Keys verwenden: t('1 Eintrag') vs. t('{count} Eintraege', { count })
  • Fehlende Keys erscheinen als [Text] (eckige Klammern = unuebersetzt)
  • Admin synchronisiert Sprachsets ueber Administration → System → UI-Sprachen → "Alle aktualisieren"

Backend (FastAPI/Python)

  • Pydantic-Models als einzige Quelle fuer UI-Feld-Definitionen
  • PowerOnModel als Basis mit System-Audit-Feldern (sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy)
  • Fehler propagieren -- Exceptions explizit werfen, nicht schlucken
  • Config ueber APP_CONFIG (aus modules/shared/configuration.py)

i18n-Pflicht: t() fuer alle UI-sichtbaren Gateway-Texte

Jeder Text der im Frontend angezeigt wird (HTTPException-Details, API-Response-Messages, Erfolgs-/Fehlermeldungen) muss mit t() getaggt werden.

from modules.shared.i18nRegistry import t

# Fehlermeldung (context automatisch = "api")
raise HTTPException(status_code=403, detail=t("Zugriff verweigert", "api.routeSecurity",
    "Fehlermeldung bei fehlendem Zugriff"))

# Erfolgsmeldung
return {"message": t("Datei erfolgreich hochgeladen", "api.routeFiles",
    "Bestaetigung nach Datei-Upload")}

Nicht mit t() taggen: Log-Eintraege, AI-Prompts, interne technische Fehlermeldungen.

Statische Dicts mit i18n-Keys: t() auf Modul-Ebene registrieren

Wenn ein Modul statische Dicts/Listen mit UI-sichtbaren Texten definiert (z.B. BADGE_DEFINITIONS, Level-Labels), muessen die Keys auf Modul-Ebene via t() registriert werden. Nur so erscheinen sie im xx-Basisset und koennen uebersetzt werden.

from modules.shared.i18nRegistry import t

# Keys auf Modul-Ebene registrieren (Import-Zeit)
_KEYS = [t("Erste Session"), t("3-Tage-Serie"), t("Wochenserie")]

# Dict mit deutschen Quelltexten als Strings (NICHT t() im Dict!)
BADGE_DEFINITIONS = {
    "first_session": {"label": "Erste Session", "icon": "star"},
    "streak_3": {"label": "3-Tage-Serie", "icon": "fire"},
}

# Bei Runtime nochmals t() aufrufen fuer aktuelle Sprache
def getBadgeDefinitions():
    return {k: {**v, "label": t(v["label"])} for k, v in BADGE_DEFINITIONS.items()}

Warum? t() registriert Keys nur beim ersten Aufruf. Wenn t() nur in einer Funktion steht, wird der Key erst beim ersten Request registriert -- nach dem Boot-Sync. Der Key fehlt dann im xx-Set und kann nicht uebersetzt werden.

TextMultilingual-Felder: Backend loest auf

TextMultilingual-Felder (User-Content in mehreren Sprachen, z.B. Role.description) werden im Backend via _resolveTextMultilingual() aufgeloest bevor sie ans Frontend geliefert werden. Das Frontend erhaelt immer einen String, nie ein Dict.

from modules.shared.i18nRegistry import _getLanguage

def _resolveTextMultilingual(value) -> str:
    if isinstance(value, str):
        return value
    if isinstance(value, dict):
        lang = _getLanguage()
        return value.get(lang) or value.get("de") or value.get("en") or ""
    return str(value) if value else ""

Frontend: Keine Dict-Fallbacks fuer Labels

Das Frontend darf keine label?.de || label?.en || Object.values(...) Ketten verwenden. Wenn das Backend korrekt arbeitet, sind alle Labels Strings. Defensive typeof === 'object' Checks verdecken Backend-Fehler und sind verboten.

// FALSCH - verdeckt Backend-Fehler
const name = typeof m.name === 'object' ? m.name.de || m.name.en : m.name;

// RICHTIG - Backend liefert String
const name = m.label || m.name || m.id;

Fuer Route-Module gibt es den Shorthand apiRouteContext:

from modules.shared.i18nRegistry import apiRouteContext
routeApiMsg = apiRouteContext("routeBilling")

raise HTTPException(status_code=403, detail=routeApiMsg("Zugriff verweigert"))

Pydantic-Models: @i18nModel Decorator Pflicht

Jedes Pydantic-Model das im UI angezeigt wird (Tabellen, Formulare) muss den @i18nModel Decorator haben. Feld-Labels werden in json_schema_extra["label"] definiert.

from modules.shared.i18nRegistry import i18nModel

@i18nModel("Benutzer")
class User(PowerOnModel):
    name: str = Field(
        description="Full name of the user",
        json_schema_extra={"label": "Name", "frontend_type": "text"}
    )
    email: str = Field(
        description="Email address for login and notifications",
        json_schema_extra={"label": "E-Mail-Adresse", "frontend_type": "text"}
    )
  • @i18nModel("Deutscher Modelname") -- AI-Kontext kommt automatisch aus dem Class-Docstring
  • json_schema_extra={"label": "Deutscher Feldname"} -- Pflicht fuer jedes UI-sichtbare Feld
  • Field(description=...) -- wird als AI-Kontext fuer die Uebersetzung verwendet
  • Kein registerModelLabels() mehr verwenden (entfernt)

Feature-Module: Labels als deutsche Basis-Strings

Neue DATA_OBJECTS, RESOURCE_OBJECTS und UI_OBJECTS Labels verwenden deutsche Klartext-Strings als Basis-Key. Bestehende {en, de, fr}-Dicts werden beim Boot automatisch ueber _registerRbacLabels() registriert (Kontext rbac.data, rbac.resource, rbac.role, rbac.quickaction). Fuer neue Eintraege: de-Text als Basis-Key verwenden, en/fr im Dict fuer Legacy-Anzeige bis vollstaendige Migration.

DATA_OBJECTS = [
    {
        "objectKey": "data.uam.UserInDB",
        "label": {"en": "User", "de": "Benutzer", "fr": "Utilisateur"},
        "meta": {"table": "UserInDB", "namespace": "uam"}
    },
]

Die de-Texte erscheinen automatisch im xx-Basisset und koennen per AI uebersetzt werden.

Projektstruktur Gateway

gateway/
  app.py                          # FastAPI-App, Middleware, Startup
  config.ini                      # Statische Konfiguration
  modules/
    auth/                         # JWT, OAuth, CSRF, Authentication
    datamodels/                   # Pydantic-Models (zentrale Quelle)
    features/                     # Feature-Module (workspace, automation, ...)
      <name>/
        main<Name>.py             # FEATURE_CODE, Registrierung
        routeFeature<Name>.py     # HTTP-Endpunkte
        interfaceFeature<Name>.py # DB-Interface
        datamodelFeature<Name>.py # Feature-spezifische Models
    interfaces/                   # DB-Interfaces (CRUD, Queries)
    routes/                       # Core-Routes (billing, admin, GDPR, ...)
    security/                     # RBAC-Engine
    serviceCenter/
      core/                       # serviceSecurity, serviceUtils, serviceStreaming
      services/                   # serviceAi, serviceChat, serviceAgent, ...
      registry.py                 # Service-Registrierung und Dependencies
    shared/                       # configuration.py, Utilities
    system/                       # registry.py (Feature-Discovery)
    workflows/
      methods/                    # Unified Action Library
      processing/                 # WorkflowProcessor, Modes
      automation/                 # v1 Runtime
      automation2/                # v2 Engine
    connectors/                   # Externe Systeme (DB, SharePoint, Jira, ...)
    aicore/                       # AI-Provider-Plugins, Model-Selector

Anti-Patterns

  • Keine impliziten Type-Conversions in API-Responses
  • Keine DB-Queries in Route-Handlern (immer ueber Interfaces)
  • Kein direkter os.environ-Zugriff -- immer APP_CONFIG
  • Keine hartkodierten Secrets -- verschluesselt in Env-Dateien