# ADR-007: Naming Conventions für CRUD-Operationen

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

## Kontext und Problemstellung

Das questra-data Package bietet CRUD-Operationen für Inventory-Items, Zeitreihen, Namespaces und weitere Ressourcen. Die Namensgebung für diese Operationen muss konsistent und pythonisch sein.

Es gibt verschiedene etablierte Ansätze:

- SQL-Terminologie: `INSERT`, `SELECT`, `UPDATE`, `DELETE`
- ORM-Patterns: Django (`create`, `filter`, `save`, `delete`), SQLAlchemy (`add`, `query`, `delete`)
- REST-Patterns: `POST`, `GET`, `PUT`, `PATCH`, `DELETE`
- Umgangssprachliche Begriffe: `fetch`, `add`, `remove`

Die Frage ist: Welche Namenskonventionen sollten konsequent im gesamten Package verwendet werden?

## Entscheidung

Wir verwenden **pythonische CRUD-Konventionen**, die an Django ORM und moderne Python-Frameworks angelehnt sind:

### Standard CRUD-Operationen

| Operation | Methode | Verwendung |
|-----------|---------|------------|
| **CREATE** | `create()` | Einzelne Ressource erstellen |
|  | `create_many()` | Mehrere Ressourcen erstellen |
|  | `save()` | Upsert (erstellen oder aktualisieren) |
| **READ** | `get()` | Einzelne Ressource per ID (wirft Exception) |
|  | `get_or_none()` | Einzelne Ressource oder None |
|  | `list()` | Multiple Ressourcen mit Filter |
|  | `filter()` | Django-Style Filterung |
|  | `all()` | Alle Ressourcen |
|  | `exists()` | Existenzprüfung |
| **UPDATE** | `update()` | Partial Update (PATCH-Semantik) |
|  | `replace()` | Full Replacement (PUT-Semantik) |
|  | `update_many()` | Bulk Update |
| **DELETE** | `delete()` | Einzelne Ressource löschen |
|  | `delete_many()` | Bulk Delete |

### Konsistenz-Prinzipien

1. **Singular für Einzeloperationen:** `create()`, `get()`, `update()`, `delete()`
2. **`_many` Suffix für Bulk-Operationen:** `create_many()`, `update_many()`, `delete_many()`
3. **Optionale Varianten mit Suffix:** `get_or_none()`, `get_or_create()`, `update_or_create()`
4. **Filter als Parameter:** `list(filter={"status": "active"}, limit=10)`

## Begründung

### Pro

- **Pythonisch:** Folgt etablierten Python-Standards (Django ORM, SQLAlchemy)
- **Objektorientiert:** `create` statt SQL-Terminologie `insert`
- **Konsistenz:** Einheitliche Begriffe über alle Ressourcen hinweg
- **Lesbarkeit:** Klare und selbsterklärende Methodennamen
- **Community-Standard:** Entspricht Erwartungen von Python-Entwicklern
- **REST-Kompatibel:** Mapping zu HTTP-Verben ist intuitiv (POST→create, GET→list/get, PATCH→update, DELETE→delete)
- **Unterscheidung single/multiple:** `create()` vs. `create_many()` ist eindeutig
- **Etabliert:** Django ORM, FastAPI, moderne Python-Frameworks nutzen diese Konventionen

### Contra

- **Nicht SQL-kompatibel:** `create` statt `INSERT` könnte SQL-Entwickler verwirren
- **Abweichung von Low-Level APIs:** Manche GraphQL/REST APIs nutzen andere Begriffe
- **Lernkurve:** Entwickler aus nicht-Python-Hintergrund müssen sich einarbeiten
- **Inkonsistenz mit Backend:** Wenn die Backend-API andere Begriffe nutzt (z.B. `insert`), entsteht eine Übersetzungsschicht

## Alternativen

### SQL-Terminologie (INSERT, SELECT, UPDATE, DELETE)

- **Pro:** Vertraut für SQL-Entwickler, direkte Mapping zu Datenbankoperationen
- **Contra:** Nicht objektorientiert, nicht pythonisch, `SELECT` ist mehrdeutig

### REST-Verben (POST, GET, PUT, PATCH, DELETE)

- **Pro:** Direkte Mapping zu HTTP-Methoden
- **Contra:** Technische Begriffe statt semantische, `POST` ist nicht selbsterklärend

### Umgangssprache (fetch, add, remove)

- **Pro:** Sehr einfach und intuitiv
- **Contra:** Nicht etabliert, inkonsistent, mehrdeutig (z.B. `add` für CREATE oder für Collection-Operation?)

### Gemischte Konventionen

- **Pro:** Flexibilität je nach Kontext
- **Contra:** Inkonsistenz, schlechte Developer Experience

## Konsequenzen

### Positiv

- Hohe Konsistenz über alle APIs (High-Level, Low-Level, Mutations)
- Pythonische und intuitive API
- Einfache Mapping zu REST-Operationen
- Vertrautes Pattern für Django/SQLAlchemy-User
- Klare Unterscheidung zwischen single- und multi-Operationen

### Negativ

- SQL-Entwickler müssen sich an `create` statt `INSERT` gewöhnen
- Übersetzungsschicht bei Backends mit anderer Terminologie
- Bei GraphQL-Mutations mit `insert`/`update` entsteht Mapping-Overhead

## Anti-Patterns

### Zu vermeidende Begriffe

| ❌ Vermeiden | Grund | ✅ Stattdessen |
|-------------|-------|---------------|
| `add()` | Zu unspezifisch, unklar ob CRUD oder Collection | `create()` |
| `insert()` | SQL-Terminologie, nicht objektorientiert | `create()` |
| `fetch()` | Umgangssprachlich, nicht etabliert | `get()` oder `list()` |
| `remove()` | Eher für Collections, inkonsistent | `delete()` |
| `find()` | Mehrdeutig (single vs. multiple?) | `get()` oder `list()` |
| `read()` | Zu generisch | `get()` oder `list()` |
| `retrieve()` | Zu formal, nicht pythonisch | `get()` |

### Inkonsistente Mischungen

```python
# ❌ Schlecht - inkonsistent
create_user()    # create
fetch_user()     # fetch
remove_user()    # remove

# ✅ Gut - konsistent
create_user()    # create
get_user()       # get
delete_user()    # delete
```

## Implementation

### High-Level API (QuestraData)

```python
from questra_data import QuestraData

# CREATE
items = client.create_items(inventory_name="TestUser", items=[{...}])
namespace = client.create_namespace(name="MyNamespace")

# READ/LIST
items = client.list_items(inventory_name="TestUser", properties=["_id", "Name"])
timeseries = client.list_timeseries_values(inventory_name="Stromzaehler", ...)
inventories = client.list_inventories(namespace_name="TestNamespace")

# UPDATE
items = client.update_items(inventory_name="TestUser", items=[{...}])

# DELETE
deleted = client.delete_items(inventory_name="TestUser", item_ids=[1, 2, 3])
result = client.delete_inventory(inventory_name="TestUser")

# UPSERT (Zeitreihen)
client.save_timeseries_values(inventory_name="Stromzaehler", ...)
```

### Low-Level API (QuestraDataCore)

```python
from questra_data import QuestraDataCore

# CREATE
items = client.inventory.create(inventory_name="TestUser", items=[{...}])
result = client.mutations.create_namespace(namespace_name="MyNamespace")

# READ/LIST
result = client.inventory.list(inventory_name="TestUser", properties=["_id"])
inventories = client.queries.get_inventories()

# UPDATE
items = client.inventory.update(inventory_name="TestUser", items=[{...}])

# DELETE
items = client.inventory.delete(inventory_name="TestUser", items=[{...}])
result = client.mutations.delete_inventory(inventory_name="TestUser")
```

## Rückgabewerte

- **Einzelne Ressource:** Gibt Objekt zurück oder wirft Exception
- **Listen:** Gibt leere Liste zurück wenn nichts gefunden
- **Bulk-Operationen:** Gibt Anzahl der betroffenen Ressourcen zurück
- **Boolean:** Nur für `exists()` und einfache Erfolgsprüfungen

## Referenzen

### Django ORM

```python
Model.objects.create()    # CREATE
Model.objects.get()       # READ single
Model.objects.filter()    # READ multiple
Model.objects.all()       # READ all
instance.save()           # UPDATE/UPSERT
instance.delete()         # DELETE
```

### SQLAlchemy

```python
session.add(obj)          # CREATE
session.query().get()     # READ single
session.query().filter()  # READ multiple
session.query().all()     # READ all
session.delete(obj)       # DELETE
```

### FastAPI/REST Mapping

```python
@app.post("/items")       # create
@app.get("/items/{id}")   # get
@app.get("/items")        # list
@app.put("/items/{id}")   # replace
@app.patch("/items/{id}") # update
@app.delete("/items/{id}") # delete
```

## Zusammenfassung

✅ **Verwende:**

- `create`, `create_many` für CREATE
- `get`, `get_or_none`, `list`, `filter`, `all`, `exists` für READ
- `update`, `replace`, `update_many` für UPDATE
- `delete`, `delete_many` für DELETE
- `save` für UPSERT

✅ **Prinzipien:**

- Konsistenz über alle Ressourcen hinweg
- Klare Unterscheidung single vs. multiple
- Pythonische Namensgebung
- Etablierte ORM-Standards folgen

❌ **Vermeide:**

- SQL-Terminologie (`insert`, `select`)
- Umgangssprache (`fetch`, `grab`)
- Inkonsistente Begriffe (`add`, `remove`)

## Notizen

Diese Konventionen wurden konsistent in allen APIs von questra-data angewendet (High-Level, Low-Level, Mutations, Inventory-Operations). Bei zukünftigen Erweiterungen sollten diese Prinzipien strikt eingehalten werden, um die API-Konsistenz zu gewährleisten.
