# ADR-005: High-Level API für vereinfachte Nutzung

**Status:** Akzeptiert
**Datum:** 2025-10-20
**Kontext:** PoC questra-data Python Package

## Kontext und Problemstellung

Das questra-data Package bietet Zugriff auf die Dyno GraphQL API und REST API. Die Low-Level API (`QuestraDataCore`) spiegelt die technische Struktur wider:

- `client.queries.*` - GraphQL Queries
- `client.mutations.*` - GraphQL Mutations
- `client.inventory.*` - Dynamische Inventory-Operationen
- `client.timeseries.*` - REST TimeSeries-Operationen
- `client.files.*` - REST File-Operationen

Diese Struktur ist technisch korrekt, erfordert aber vom User:

- Verständnis der internen Architektur (GraphQL vs REST)
- Mehrschrittige Workflows für häufige Use-Cases
- Manuelle Kombinationen von Operations (z.B. Items laden + TimeSeries-Daten abrufen)

Die Frage ist: Sollte es eine vereinfachte, task-orientierte API geben?

## Entscheidung

Wir führen eine **High-Level API** (`QuestraData`) ein, die:

- Häufige Use-Cases vereinfacht
- Die technischen Details (GraphQL vs REST) abstrahiert
- Mehrschrittige Workflows in einer Methode kapselt
- Die Low-Level API (`QuestraDataCore`) intern nutzt und weiterhin zugänglich macht

Architektur:

```python
# High-Level API (QuestraData) - für die meisten User
client = QuestraData(...)
values = client.list_timeseries_values(...)  # Kombiniert automatisch mehrere Low-Level Calls

# Low-Level API (QuestraDataCore) - für Power-User
lowlevel = client.lowlevel
result = lowlevel.queries.get_inventories(...)
```

## Begründung

### Pro

- **Developer Experience:** User müssen nicht verstehen, ob eine Operation via GraphQL oder REST läuft
- **Weniger Boilerplate:** Häufige Workflows in einer Methode statt mehreren Low-Level Calls
- **Pragmatische Defaults:** Sinnvolle Standardwerte für häufige Parameter
- **Bessere Dokumentation:** High-Level Docstrings können User-Stories beschreiben, nicht technische Details
- **pandas-Integration:** High-Level API kann `_df()`-Varianten anbieten (z.B. `list_items_df()`)
- **Einfachere Migration:** Bei Breaking Changes in der Low-Level API kann High-Level API Kompatibilität wahren
- **Beispiel-Code:** Tutorials und Docs können sich auf die einfachere High-Level API konzentrieren
- **Reduzierte Komplexität:** User mit einfachen Use-Cases müssen die Low-Level API nicht lernen

**Konkretes Beispiel:**

```python
# Low-Level API - 3 Schritte manuell:
# 1. Items mit TimeSeries-Properties laden
items = client.inventory.list(inventory_name="Sensoren", properties=["_id", "messwerte.id"])
# 2. TimeSeries-IDs extrahieren
ts_ids = [item["messwerte"]["id"] for item in items["nodes"]]
# 3. Zeitreihen-Daten laden
data = client.timeseries.get_data(time_series_ids=ts_ids, from_time=..., to_time=...)
# 4. Manuell kombinieren
# ... komplexe Mapping-Logic

# High-Level API - 1 Schritt:
result = client.list_timeseries_values(
    inventory_name="Sensoren",
    timeseries_properties="messwerte",
    from_time=...,
    to_time=...
)
# Ergebnis ist bereits kombiniert und strukturiert
```

### Contra

- **Zusätzlicher Wartungsaufwand:** Zwei APIs müssen synchron gehalten werden
- **Versteckte Komplexität:** User wissen nicht, wie viele Requests intern durchgeführt werden
- **Performance-Überraschungen:** High-Level Methoden könnten ineffizient sein bei großen Datenmengen
- **Flexibilitätsverlust:** High-Level API kann nicht alle Low-Level Szenarien abdecken
- **Lernkurve:** User müssen entscheiden, wann High-Level und wann Low-Level API zu verwenden ist
- **Mehr Code:** Package wird größer durch zusätzliche Abstraktionsschicht
- **Potentielle Inkonsistenzen:** Wenn High-Level und Low-Level API unterschiedlich funktionieren

## Alternativen

### Nur Low-Level API

- **Pro:** Keine zusätzliche Komplexität, direkte Kontrolle
- **Contra:** Schlechtere Developer Experience, mehr Boilerplate

### Nur High-Level API

- **Pro:** Einheitliche API, einfacher zu lernen
- **Contra:** Power-User haben keine Fine-Grained-Kontrolle

### Helper-Functions statt Class

- **Pro:** Kein zusätzliches Objekt nötig
- **Contra:** Weniger OOP-typisch, schlechtere Discoverability

## Konsequenzen

### Positiv

- Schnellerer Einstieg für neue User (Getting Started mit High-Level API)
- Weniger Code in User-Anwendungen (10+ Zeilen → 1-3 Zeilen)
- pandas-Integration ohne zusätzliche Dependencies im Core
- Use-Case-orientierte Dokumentation möglich
- Low-Level API bleibt für Power-User verfügbar

### Negativ

- Zusätzlicher Wartungsaufwand beim Package-Maintainer
- Bei Breaking Changes in Low-Level API muss High-Level API angepasst werden
- User könnten Performance-Probleme nicht erkennen (versteckte N+1-Queries)

## Implementation

```python
# High-Level Client
from questra_data import QuestraData

client = QuestraData(
    graphql_url="https://dev.example.com/graphql/",
    auth_client=auth_client
)

# Vereinfachte API
values = client.list_timeseries_values(...)
items = client.list_items(...)
client.create_items(...)

# Optional: pandas-Integration
df = client.list_timeseries_values_df(...)

# Low-Level API für Power-User
lowlevel = client.lowlevel
lowlevel.queries.get_inventories(...)
lowlevel.mutations.create_inventory(...)
```

## Abgrenzung zu ursprünglichem Vorschlag

Die ursprüngliche Diskussion in ADR.md bezog sich auf die Zusammenführung von `queries` und `mutations` in der **Low-Level API**. Diese Entscheidung wurde **nicht** umgesetzt - die Low-Level API behält die Trennung bei:

```python
client.lowlevel.queries.*     # Weiterhin getrennt
client.lowlevel.mutations.*   # Weiterhin getrennt
client.lowlevel.inventory.*   # Dynamische Operations
```

Stattdessen wurde eine **High-Level API** eingeführt, die:

- Die technische Trennung (queries/mutations/REST) für User irrelevant macht
- Task-orientierte Methoden anbietet (z.B. `create_items()`, `list_timeseries_values()`)
- Die Low-Level API intern nutzt, aber nicht deren Struktur spiegelt

Begründung für diesen Ansatz statt der Zusammenführung von queries/mutations:

- **Separation of Concerns bleibt erhalten:** Low-Level API spiegelt weiterhin GraphQL-Struktur (Read vs Write)
- **Keine Breaking Changes:** Bestehende Low-Level API bleibt unverändert
- **Klarere Verantwortlichkeiten:** Low-Level = technisch korrekt, High-Level = user-friendly
- **Flexibilität:** Power-User können Low-Level API weiterhin verwenden, Anfänger nutzen High-Level API
- **Testbarkeit:** High-Level API kann Mock-Low-Level-Client nutzen, interne Struktur bleibt testbar

## Notizen

Die High-Level API ist optional - User können das Package weiterhin mit der Low-Level API (`QuestraDataCore`) verwenden. Die Dokumentation sollte beide Ansätze zeigen:

- **Getting Started / Tutorials:** High-Level API
- **Advanced Usage / API Reference:** Low-Level API

Bei neuen Features sollte zuerst die Low-Level API implementiert werden, dann optional eine High-Level Wrapper-Methode, wenn der Use-Case häufig vorkommt.
