# ADR-003: Umbrella Package Design mit Convenience Re-Exports

**Status**: Accepted
**Datum**: 2025-11-11
**Entscheider**: Development Team
**Kontext**: API-Design für das questra Umbrella-Package

## Kontext und Problem

Das `questra` Repository enthält mehrere Sub-Pakete als Git-Submodules:

- `questra-authentication`: OAuth2 Authentifizierung
- `questra-data`: GraphQL + REST Data API
- Geplant: `questra-automation`, `questra-email`

Aktuell deklariert das Umbrella-Paket nur Dependencies auf die Sub-Pakete ([pyproject.toml:11-12](../pyproject.toml#L11-L12)), exponiert aber keine eigene API. [src/questra/\_\_init__.py](../src/questra/__init__.py) ist leer.

### Problem

Benutzer müssten:

1. Mehrere Pakete einzeln installieren
2. Korrekte Import-Pfade kennen (`questra_authentication` vs `questra-authentication`)
3. Kompatible Versionen manuell auswählen

### Anforderungen

1. **Single Entry Point:** Benutzer sollen nur `questra` installieren müssen
2. **Intuitive Imports:** Vereinfachte API ohne Kenntnis der Sub-Paket-Struktur
3. **Skalierbarkeit:** Einfache Integration weiterer Sub-Pakete (automation, email)
4. **POC-Phase:** Breaking Changes sind aktuell normal und kein Problem
5. **Zukunftssicher:** Nach Release sollen Breaking Changes minimiert werden

## Betrachtete Optionen

1. **Convenience Re-Export** - Re-exportiert häufig benötigte Komponenten
2. **Factory Pattern** - Zentralisierte Client-Initialisierung
3. **Namespace Package (Minimalistisch)** - Nur Dependency-Aggregation
4. **Plugin Registry** - Auto-Discovery via Entry Points

---

## Option 1: Convenience Re-Export (GEWÄHLT)

### Beschreibung

Das Umbrella-Paket re-exportiert Core-Clients und häufig verwendete Models für vereinfachten Import.

### Implementierung

```python
# src/questra/__init__.py
from questra_authentication import QuestraAuthentication
from questra_data import QuestraData, QuestraDataCore

from questra_data import (
    Inventory, Namespace, TimeSeriesValue,
    SortOrder, ConflictAction
)

__all__ = [
    "QuestraAuthentication",
    "QuestraData",
    "QuestraDataCore",
    "Inventory",
    "Namespace",
    # ...
]
```

### PRO

- **Bessere Developer Experience:** Weniger Import-Pfade zu merken
- **Klare Haupt-API:** `__all__` definiert empfohlene Nutzung
- **Flexibilität:** Direkter Zugang zu Sub-Paket-APIs bleibt möglich
- **IDE-Support:** Auto-Completion funktioniert out-of-the-box
- **Skalierbar:** Neue Sub-Pakete einfach zu integrieren

### CONTRA

- **Maintenance-Overhead:** Breaking Changes in Sub-Paketen müssen reflektiert werden
- **Versionsabhängigkeiten:** Synchronisation zwischen Umbrella und Sub-Paketen
- **Namespace-Pollution:** `__all__` kann groß werden

### Verwendungsbeispiel

```python
# Einfach
from questra import QuestraAuthentication, QuestraData

auth = QuestraAuthentication(url="...", username="...", password="...")
client = QuestraData(graphql_url="...", auth_client=auth)
items = client.list(inventory_name="Assets", namespace="Production")

# Fortgeschritten (direkter Sub-Paket-Zugriff)
from questra_data.models import InventoryPrivilege
```

---

## Option 2: Factory Pattern

### Beschreibung

Umbrella-Paket bietet Factory-Funktion für vereinfachte Client-Initialisierung.

### Implementierung

```python
def create_client(
    graphql_url: str,
    auth_url: str | None = None,
    username: str | None = None,
    password: str | None = None,
    auth_client: QuestraAuthentication | None = None,
) -> QuestraData:
    if auth_client is None:
        auth_client = QuestraAuthentication(
            url=auth_url, username=username, password=password
        )
    return QuestraData(graphql_url=graphql_url, auth_client=auth_client)
```

### PRO

- **Weniger Boilerplate:** Auth + Client in einem Schritt
- **Best-Practice-Defaults:** Zentrale Konfiguration

### CONTRA

- **Weniger flexibel:** Versteckt Sub-Paket-APIs
- **Eingeschränkt:** Nicht alle Use-Cases abdeckbar

---

## Option 3: Namespace Package (Minimalistisch)

### Beschreibung

Umbrella-Paket bleibt leer, dokumentiert nur Installation und Versionskombinationen.

### Implementierung

```python
# src/questra/__init__.py
"""
Questra - Meta-Package für Questra Python Clients

Verwendung:
    from questra_authentication import QuestraAuthentication
    from questra_data import QuestraData
"""
__version__ = "0.2.2"
```

### PRO

- **Kein Code-Overhead:** Minimale Wartung
- **Klare Trennung:** Sub-Pakete bleiben eigenständig

### CONTRA

- **Kein technischer Mehrwert:** Nur Dependency-Aggregation
- **Benutzer müssen alle Import-Pfade kennen**

---

## Option 4: Plugin Registry

### Beschreibung

Umbrella-Paket bietet Plugin-System für zukünftige Extensions.

### Implementierung

```python
class QuestraRegistry:
    def __init__(self):
        self._plugins: dict[str, QuestraPlugin] = {}
        self._discover_plugins()

registry = QuestraRegistry()
```

### PRO

- **Zukunftssicher:** Skaliert für viele Sub-Pakete
- **Auto-Discovery:** Entry-Points-basiert

### CONTRA

- **Overengineering:** Zu komplex für aktuell 2 Sub-Pakete
- **Hoher initialer Aufwand**

---

## Entscheidung

**Option 1: Convenience Re-Export**

### Begründung

- Beste Balance zwischen Einfachheit und Funktionalität
- Erfüllt Anforderung "Single Entry Point" ohne Komplexität
- Skaliert gut für geplante Sub-Pakete (automation, email)
- POC-freundlich: Keine komplexen Abstractions

### Implementierungsdetails

#### Re-Exports

```python
# Core Clients (Top-Level)
from questra_authentication import QuestraAuthentication
from questra_data import QuestraData, QuestraDataCore

# Häufig verwendete Models
from questra_data import (
    Inventory, InventoryProperty, Namespace,
    TimeSeriesValue, SortOrder, ConflictAction
)

# Exceptions
from questra_authentication import (
    AuthenticationError, InvalidCredentialsError
)
```

#### Zukünftige Sub-Pakete (optional)

```python
# Optional dependencies
try:
    from questra_automation import QuestraAutomation
    __all__.append("QuestraAutomation")
except ImportError:
    pass
```

#### Dependency Management

**POC-Phase:**

```toml
questra-authentication = {version = ">=0.1.5,<0.3.0", source = "testpypi"}
questra-data = {version = ">=0.5.3,<0.7.0", source = "testpypi"}
```
- Erlaubt 1-2 Minor-Versionen für Breaking Changes

**Nach Release:**

```toml
questra-authentication = {version = "^0.1.5", source = "testpypi"}
questra-data = {version = "^0.5.3", source = "testpypi"}
```

- Nur Patch-Updates (Semantic Versioning)

### Versioning-Strategie

#### POC-Phase (aktuell)

| Ereignis | Umbrella | Sub-Paket | Begründung |
|----------|----------|-----------|------------|
| Neue Funktion | 0.2.3 (Patch) | 0.5.4 (Patch) | Feature-Update |
| Breaking Change | 0.3.0 (Minor) | 0.6.0 (Minor) | POC erlaubt häufige Breaks |
| Neues Sub-Paket | 0.3.0 (Minor) | - | API-Erweiterung |

#### Nach Release (≥1.0.0)

| Ereignis | Umbrella | Sub-Paket | Begründung |
|----------|----------|-----------|------------|
| Neue Funktion | 1.1.0 (Minor) | 1.1.0 (Minor) | Rückwärtskompatibel |
| Breaking Change in `__all__` | 2.0.0 (Major) | 2.0.0 (Major) | Public API gebrochen |
| Bugfix | 1.0.1 (Patch) | 1.0.1 (Patch) | Keine API-Änderung |

## Konsequenzen

### Positiv

- **Einfache Installation:** `pip install questra` → alles verfügbar
- **Bessere Developer Experience:** Intuitive Imports
- **Klare Haupt-API:** `__all__` definiert empfohlene Nutzung
- **Flexibilität:** Direkter Zugang zu Sub-Paket-APIs bleibt
- **Skalierbar:** Neue Sub-Pakete einfach zu integrieren

### Negativ

- **Maintenance-Overhead:** Breaking Changes in Sub-Paketen müssen im Umbrella reflektiert werden
- **Versionsabhängigkeiten:** Umbrella-Version muss mit Sub-Paket-Versionen synchronisiert werden
- **Namespace-Pollution:** `__all__` kann groß werden bei vielen Sub-Paketen

### Neutral

- **Dokumentation:** `__init__.py` muss Verwendungsbeispiele enthalten
- **Testing:** Umbrella-Paket benötigt Import-Tests (keine Funktionalitäts-Tests)
- **CI/CD:** Automatisches Dependency-Update bei Sub-Paket-Releases empfohlen

## Risiken und Mitigations

| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|--------|-------------------|--------|------------|
| Breaking Change in Sub-Paket bricht Umbrella | Hoch (POC) | Mittel | Dependency-Range erlaubt mehrere Minor-Versionen |
| Vergessene Exports in `__all__` | Mittel | Niedrig | Integration-Tests für alle Public APIs |
| Version-Mismatch zwischen Sub-Paketen | Niedrig | Hoch | Automatische Dependency-Updates via CI |
| Performance-Overhead durch Re-Exports | Sehr niedrig | Sehr niedrig | Python-Import-Caching |

## Alternativen

Falls sich Option 1 als problematisch erweist:

1. **Fallback:** Option 3 (Namespace Package) - kein Code, nur Dependencies
2. **Erweiterung:** Option 2 (Factory) zusätzlich zu Re-Exports für spezielle Use-Cases

## Verweise

- [pyproject.toml](../pyproject.toml#L11-L12): Aktuelle Dependencies
- [README.md](../README.md): Workflow-Dokumentation
- [ADR-001](ADR-001-multi-project-documentation.md): Multi-Project Documentation
- Zukünftige Sub-Pakete: `questra-automation`, `questra-email`

## Offene Fragen

1. ~~Wie häufig sind Breaking Changes zu erwarten?~~ → POC-Phase: häufig, nach Release: selten
2. ~~Werden weitere Sub-Pakete hinzukommen?~~ → Ja: automation, email (kurzfristig)
3. Sollen Sub-Pakete einzeln installierbar bleiben? → Ja (für spezielle Use-Cases)
4. Brauchen wir Compatibility-Matrix in Docs? → Nach Release: Ja
