Spring Data, JDBC, JPA & Read Models

Repository-Methoden, Projections, Specifications, JdbcClient, CQRS-light und saubere Query-Grenzen.

Version 3SpringCodeDiagramm
Diagramm Spring Data, JDBC, JPA & Read Models
Fachlich-technische Darstellung zu Spring Data, JDBC, JPA & Read Models.

Repository ist keine Abkürzung um Use Cases herum

Spring Data spart Code, aber Controller sollten nicht direkt Repositories orchestrieren. Der Use Case definiert, welche Daten fachlich gebraucht werden, und das Repository liefert eine passende Abfrage.

Read Model statt Entity-Leak

Listenansichten, Suchseiten und Reports brauchen selten komplette Aggregates. Projections oder eigene Read Models reduzieren Datenvolumen und verhindern Lazy-Loading-Fallen.

JPA und JDBC kombinieren

JPA eignet sich für Aggregate und transaktionale Änderungen. JDBC oder SQL-Projections sind oft klarer für komplexe Reports, Massenabfragen und optimierte Read-Modelle.

Entscheidungen

EntscheidungGute PraxisPrüffrage
Fachliche GrenzeZuerst Use Case, Invariante und Verantwortlichkeit klären.Welche Geschäftsentscheidung wird geschützt?
Technische GrenzeFramework-/Library-Code hinter Port, Adapter oder Konfiguration kapseln.Kann die Domain ohne Framework getestet werden?
BetriebTimeouts, Logs, Metriken, Traces, Security und Rollback definieren.Wie erkennt der Betrieb Fehler rechtzeitig?

Ausführliche Beispiele

Projection für Listen-API
public interface OrderListProjection {
    UUID getId();
    String getCustomerNumber();
    BigDecimal getTotalAmount();
    Instant getCreatedAt();
}

interface OrderJpaRepository extends JpaRepository<OrderEntity, UUID> {
    List<OrderListProjection> findByStatusOrderByCreatedAtDesc(OrderStatus status);
}
JdbcClient für explizites Read Model
public List<OrderListView> findOpenOrdersForDashboard() {
    return jdbcClient.sql("""
        select o.id, c.customer_number, o.total_amount, o.created_at
          from orders o
          join customers c on c.id = o.customer_id
         where o.status = :status
         order by o.created_at desc
         limit 100
        """)
        .param("status", "OPEN")
        .query(OrderListView.class)
        .list();
}

Typische Stolperfallen

StolperfalleWarum gefährlich
Derived Query wird RomanLange Methodennamen sind schwer wartbar.
Entity als API-VertragPersistenzdetails werden öffentlich.
Keine Index-StrategieGute Java-Abfrage hilft nicht bei schlechter DB-Struktur.
⌂ Cockpit