# Reflex State-Architektur — Full-Stack-Python-Webapps ohne JavaScript entwickeln

> Wie Reflex State-Management in der Praxis funktioniert: State-Graphen entwerfen die skalieren, asynchrone Events und Background-Tasks, Datenbankintegrations-Muster und das Deployment-Modell für WebSocket-basierte Python-Web-Applikationen.

For AI agents: curated site map at [https://www.devolute.org/llms.txt](https://www.devolute.org/llms.txt). Canonical HTML (preferred for search engines): [https://www.devolute.org/de/themen/open-source/reflex-python-webframework-state-architektur](https://www.devolute.org/de/themen/open-source/reflex-python-webframework-state-architektur).

- Language: de
- Published: 2026-06-11
- Markdown URL: https://www.devolute.org/de/themen/open-source/reflex-python-webframework-state-architektur.md

## Reflex State-Architektur: die Muster, die funktionierend von fragil trennen

State-Graph-Design, async Events, Datenbankintegration und WebSocket-Deployment — die Entscheidungen, die bestimmen, ob eine Reflex-App wächst oder stagniert.

Reflex nimmt eine andere Position ein als Streamlit oder Gradio. Es ist kein Dashboard-Tool oder Modell-Demo-Framework — es ist ein Full-Stack-Web-Framework, das Python zu React kompiliert und State-Änderungen über WebSockets kommuniziert. Der korrekte Vergleich ist Next.js, SvelteKit oder Django mit HTMX, nicht Streamlit.

Das bedeutet, dass auch die Engineering-Aspekte andere sind: State-Graph-Design, Event-Handler-Isolation, Datenbankintegration, Background-Tasks und WebSocket-bewusstes Deployment spielen in einer Weise eine Rolle, die bei Streamlit oder Gradio nicht anfällt.

## Wie Reflex State funktioniert

Reflex-State ist eine Python-Klasse, die von `rx.State` erbt. Felder der Klasse sind reaktiv: wenn ein Event-Handler ein Feld mutiert, vergleicht Reflex alten und neuen State, serialisiert das Delta und pusht es über einen WebSocket an den Browser. Das React-Frontend wendet das Delta an, ohne vollständig neu zu rendern.

```python
class AppState(rx.State):
    items: list[str] = []
    loading: bool = False

    async def add_item(self, item: str):
        self.loading = True
        yield  # loading=True sofort an den Client flushen
        self.items.append(item)
        self.loading = False
```

Das `yield` innerhalb eines Event-Handlers ist bedeutsam: es flusht das aktuelle State-Delta an den Client, ohne den Handler zu beenden. Das ermöglicht Fortschrittsanzeigen, gestreamte Teilergebnisse und jede Interaktion, die Zwischen-UI-Updates während einer langen Operation braucht.

## State-Graph-Design: was wohin gehört

Der häufigste Reflex-Fehler: alles in eine einzige State-Klasse packen. Mit wachsender App entsteht ein State-Objekt, das groß ist (alles wird pro Verbindung serialisiert), langsam zu vergleichen (jedes Event berechnet Diffs über den gesamten State) und unmöglich isoliert zu testen ist.

Das korrekte Muster: **mehrere State-Klassen mit expliziten Substates**.

```python
class AuthState(rx.State):
    user_id: str = ""
    user_role: str = ""

class DashboardState(AuthState):
    data: list[dict] = []
    filter_value: str = ""

class SettingsState(AuthState):
    theme: str = "light"
    notifications_enabled: bool = True
```

`DashboardState` erbt `AuthState`, hat also Zugang zu `user_id` und `user_role`, ohne sie zu duplizieren. Reflex serialisiert jeden Substate unabhängig und vergleicht nur den betroffenen Substate bei jedem Event. Eine Filteränderung in `DashboardState` erzwingt keinen Diff von `SettingsState`.

Regeln für State-Graph-Design:

1. **UI-only-State wenn möglich auf Komponentenebene halten.** Reflex 0.4+ unterstützt lokalen Komponenten-State via `rx.ComponentState`. Verwenden Sie ihn für Toggle offen/geschlossen, Tab-Auswahl und jeden State, der nicht geteilt werden muss.
2. **Geteilten State in den nächsten gemeinsamen Vorfahren.** Wenn zwei Komponenten auf verschiedenen Seiten denselben Wert brauchen, gehört er in einen gemeinsamen Eltern-State.
3. **Backend-only-Werte vollständig aus dem State heraushalten.** Datenbankverbindungen, API-Clients und Modell-Weights gehören nicht in `rx.State`. Sie kommen in Modul-level-Singletons oder Dependency-Injection-Muster.

## Async Events und Background-Tasks

Reflex-Event-Handler können async sein. Für Operationen, die mehr als ein paar Millisekunden dauern — Datenbankabfragen, API-Calls, Modell-Inferenz — ist async Pflicht:

```python
async def load_data(self):
    self.loading = True
    yield
    async with httpx.AsyncClient() as client:
        response = await client.get(self.api_url)
    self.data = response.json()
    self.loading = False
```

Für Operationen, die den UI-Thread überhaupt nicht blockieren sollen — E-Mails versenden, Reports ausführen, Uploads verarbeiten — `rx.background` verwenden:

```python
@rx.background
async def process_upload(self, file_data: bytes):
    result = await heavy_processing(file_data)
    async with self:  # State-Lock wieder erlangen, um zu mutieren
        self.result = result
        self.processing = False
```

`rx.background` führt den Handler als Background-Task aus. Der `async with self`-Block erlangt den State-Lock wieder, um State sicher von außerhalb des normalen Event-Dispatch-Zyklus zu mutieren. Das ist notwendig, weil Reflex-State nicht thread-sicher ist — alle Mutationen müssen durch den State-Lock.

## Datenbankintegration

Reflex enthält eine eingebaute SQLAlchemy-Integration via `rx.Model`. Für einfache CRUD-Applikationen ist das praktisch. Für alles jenseits von einfachem CRUD — komplexe Queries, async Treiber, auf die Last abgestimmtes Connection-Pooling — SQLAlchemy direkt mit einem async Engine verwenden:

```python
engine = create_async_engine(DATABASE_URL, pool_size=10, max_overflow=5)

async def get_users_by_role(role: str) -> list[dict]:
    async with AsyncSession(engine) as session:
        result = await session.execute(
            select(User).where(User.role == role)
        )
        return [u.__dict__ for u in result.scalars()]
```

Aus Event-Handlern aufrufen. Die Session wird pro Call erstellt und geschlossen, was für Web-Request-Muster korrekt ist. Keine Session in `rx.State` speichern — SQLAlchemy-Sessions sind nicht serialisierbar.

## WebSocket-Deployment

Reflex kommuniziert via WebSocket. Das hat Deployment-Implikationen, die sich von einer Standard-HTTP-API unterscheiden:

**Sticky Sessions sind für Multi-Instance-Deployments erforderlich.** Jede Reflex-Verbindung pflegt Server-seitigen State, der an einen bestimmten Prozess gebunden ist. Ein Load-Balancer, der Requests Round-Robin zwischen Instanzen verteilt, bricht aktive Verbindungen. Cookie-basierte Session-Affinity in nginx, AWS ALB oder GCP Cloud Run konfigurieren.

**WebSocket-Timeout-Konfiguration.** Standard-nginx-Proxy-Timeouts (60s) terminieren idle WebSocket-Verbindungen. `proxy_read_timeout` auf einen Wert länger als die längste erwartete Idle-Periode setzen (z.B. `proxy_read_timeout 3600s;` für Anwendungen, wo Nutzer einen Tab offen lassen können).

**Frontend und Backend deployen atomar.** Reflex kompiliert das Frontend zum Build-Zeitpunkt. Ein Mismatch zwischen dem Frontend-JavaScript und dem Backend-Python-State-Protokoll verursacht subtile Bugs — State-Updates, die zu funktionieren scheinen, aber die UI korrumpieren. Die CI/CD-Pipeline muss beides zusammen deployen, nie unabhängig.

**Redis für State in Multi-Instance-Setups.** Standardmäßig speichert Reflex State im Prozess. Für horizontale Skalierung ein Redis-State-Backend konfigurieren. Das verlagert State aus dem Prozess und erlaubt mehreren Reflex-Instanzen, ihn zu teilen — fügt aber auch Serialisierungs-Overhead pro Event hinzu. Benchmarken, bevor angenommen wird, dass Redis Multi-Instance-Reflex unkompliziert macht.

## Wofür Reflex nicht ausgelegt ist

- **SEO-kritische öffentliche Seiten.** Reflex rendert client-seitig. Suchmaschinen-Crawler sehen eine weitgehend leere HTML-Hülle. Wenn organische Suche wichtig ist, Marketing-Seiten auf einer statischen Site oder einem SSR-Framework hosten und Reflex für die authentifizierte Anwendungsshell verwenden.
- **Hochfrequente Echtzeit-Daten.** WebSocket ist das richtige Transport-Protokoll, aber Reflexs State-Diff-Modell ist für Nutzerinteraktions-Frequenz optimiert (Klicks, Formular-Absenden), nicht für Sensor-Daten oder Trading-Feeds, die hunderte Male pro Sekunde aktualisieren.
- **Große File-Uploads oder Downloads.** Diese über einen Signed URL zu S3/GCS routen statt über die Reflex-WebSocket-Verbindung.

## Mehr Lesen

- [Streamlit vs. Gradio vs. Reflex — welches Framework passt?](/de/themen/open-source/streamlit-vs-gradio-vs-reflex-de)
- [Reflex-Python-Webapp-Entwicklung](/de/agentur/reflex-python-webapp-entwicklung-muenchen)
- [Python- und Django-Agentur Berlin](/de/agentur/python-django-agentur-in-berlin)
- [Open-Source LLM Agents Orchestrierung](/de/agentur/open-source-llm-agents-orchestration)
