meldestelle/docs/05_Backend/Guides/Database_Best_Practices.md

60 lines
2.4 KiB
Markdown

---
type: Guide
status: DRAFT
owner: Backend Developer
date: 2026-02-02
last_update: 2026-03-15
---
# Database Best Practices & Exposed 1.0.0
Dieser Guide beschreibt den korrekten Umgang mit der Datenbank-Schicht in unseren Backend-Services, basierend auf JetBrains Exposed 1.0.0.
## 1. Architektur-Prinzipien
* **Trennung:** Datenbank-Zugriffe gehören ausschließlich in die `infrastructure/persistence` Schicht. Services nutzen Repositories (Interfaces), keine direkten Exposed-Aufrufe.
* **Transaktionen:** Jede geschäftliche Operation sollte in einer Transaktion laufen. Nutze dafür die Helper aus `DatabaseUtils.kt`.
## 2. Nutzung von `DatabaseUtils`
Wir haben zentrale Wrapper für Transaktionen, um Fehlerbehandlung und Logging zu vereinheitlichen.
### 2.1 Transaktionen starten
Nutze immer `transactionResult` (oder die Aliase `readTransaction` / `writeTransaction`), um Exposed-Code auszuführen.
```kotlin
fun findUser(id: UUID): Result<User> = readTransaction {
// 'this' ist hier eine JdbcTransaction
UserTable.select { UserTable.id eq id }
.map { /* row -> User(...) */ }
.singleOrNull()
}
```
**Wichtig:** Der Lambda-Receiver ist `JdbcTransaction`. Das ermöglicht Zugriff auf Low-Level JDBC Funktionen, falls nötig.
### 2.2 Low-Level SQL (`exec`, `executeUpdate`)
Vermeide rohes SQL, wo immer möglich. Wenn es sein muss (z.B. für Performance-Optimierungen oder spezielle Postgres-Features), beachte folgende Regeln für Exposed 1.0.0:
* **`exec`:** Nutze immer `explicitStatementType`.
```kotlin
this.exec("SELECT 1", explicitStatementType = StatementType.SELECT) { rs -> /* handle ResultSet */ }
```
* **`executeUpdate`:** Nutze die Helper-Methode `DatabaseUtils.executeUpdate`, da sie sich um das korrekte Schließen von Statements kümmert (Exposed `PreparedStatementApi` ist nicht `AutoCloseable`).
## 3. Exposed 1.0.0 Besonderheiten
* **UUIDs:** Nutze `Table.javaUUID()` für `java.util.UUID` Spalten. `Table.uuid()` ist für `kotlin.uuid.Uuid` reserviert.
* **JSONB:** Bei SQLite wird JSON automatisch gewrappt. Prüfe `castToJsonFormat` Flag.
## 4. Fehlerbehandlung
`DatabaseUtils` fängt `SQLException` ab und mappt sie auf unsere Domain-Fehler (`ErrorDto`):
* Duplicate Key -> `ErrorCodes.DUPLICATE_ENTRY`
* Foreign Key -> `ErrorCodes.FOREIGN_KEY_VIOLATION`
* Timeout -> `ErrorCodes.DATABASE_TIMEOUT`
Wirf keine rohen Exceptions aus Repositories.