Microservices sind produktiv, aber ihre Infrastruktur ist komplex: Service Discovery, mTLS, State Management, Pub/Sub, Retries, Circuit Breaker, verteiltes Tracing — das alles muss jede Anwendung selbst oder über Bibliotheken integrieren. Genau hier setzt Dapr (Distributed Application Runtime) an: Es verlagert die gesamte Infrastruktur-Logik in einen Sidecar-Prozess, und dein Java-Code kommuniziert nur noch mit localhost.
Dieser Artikel zeigt, wie du Dapr 1.18 mit Spring Boot und dem Java SDK 1.17.4 einsetzt.
Was ist Dapr?
Dapr ist eine Open-Source-Laufzeitumgebung (CNCF Incubating), die als Sidecar neben deiner Anwendung läuft. Statt Redis-Client, Kafka-Producer und Service-Discovery-Logik in deine App zu packen, sprichst du die Dapr-API auf http://localhost:3500 (HTTP) oder localhost:50001 (gRPC) an.
Die Architektur:
┌─────────────┐ ┌─────────────┐
│ order-svc │ │ payment-svc │
│ (Spring) │ │ (Spring) │
└──────┬──────┘ └──────┬──────┘
│ gRPC/HTTP │
┌────▼─────┐ ┌────▼─────┐
│ Dapr │◄── mTLS ──►│ Dapr │
│ Sidecar │ │ Sidecar │
└────┬─────┘ └────┬─────┘
│ │
┌────▼─────┐ ┌────▼─────┐
│ Redis │ │ Kafka │
│ State │ │ Pub/Sub │
└──────────┘ └──────────┘
Installation und erster Start
<em># macOS</em>
brew install dapr/tap/dapr-cli
<em># Dapr initialisieren (startet Redis, Zipkin, Placement)</em>
dapr init
<em># Verifizieren</em>
dapr --version <em># CLI 1.18, Runtime 1.18</em>
Code-Sprache: HTML, XML (xml)
Eine Spring-Boot-App mit Dapr starten:
dapr run --app-id order-service --app-port 8080 --dapr-http-port 3500 \
-- mvn spring-boot:run
Code-Sprache: CSS (css)
Java SDK Setup
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-bom</artifactId>
<version>1.18.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk</artifactId>
<version>1.17.4</version>
</dependency>
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-springboot</artifactId>
<version>1.17.4</version>
</dependency>
</dependencies>
Code-Sprache: HTML, XML (xml)
application.properties:
dapr.client.httpEndpoint=http://localhost
dapr.client.httpPort=3500
dapr.client.grpcEndpoint=localhost
dapr.client.grpcPort=50001
dapr.statestore.name=statestore
dapr.pubsub.name=pubsub
Code-Sprache: JavaScript (javascript)
State Management: Persistieren ohne Datenbank-Treiber
Dapr abstrahiert State Stores (Redis, PostgreSQL, CosmosDB, etc.) — dein Code bleibt datenbank-agnostisch:
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.State;
<em>// Client bauen (in Spring Boot per @Autowired verfügbar)</em>
DaprClient client = new DaprClientBuilder().build();
<em>// Speichern</em>
client.saveState("statestore", "order-42", new Bestellung("42", "Monitor", 349.0))
.block();
<em>// Lesen</em>
State<Bestellung> state = client.getState("statestore", "order-42", Bestellung.class)
.block();
Bestellung b = state.getValue();
<em>// Löschen</em>
client.deleteState("statestore", "order-42").block();
Code-Sprache: JavaScript (javascript)
Der Standard-Komponenten-Name statestore wird in ~/.dapr/components/statestore.yaml definiert und zeigt auf Redis. In Produktion änderst du nur diese YAML-Datei:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis-prod.meinefirma.com:6379
Service Invocation: Services aufrufen ohne Service Discovery
<em>// Service-Aufruf mit HTTP</em>
DaprInvokeHttpClient invoker = client.invokeHttpClient("payment-service");
HttpRequest request = invoker.newRequestBuilder("pay")
.header("Content-Type", "application/json")
.POST(DaprBodyPublishers.json(new Zahlung("order-42", 349.0)))
.build();
HttpResponse<String> response = invoker.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Code-Sprache: JavaScript (javascript)
Der Aufruf geht an payment-service — ohne feste Host/Port-Konfiguration. Dapr übernimmt Service Discovery (via mDNS lokal, Kubernetes-DNS im Cluster), mTLS und automatische Retries.
Pub/Sub: Entkoppelte Services via Events
<em>// Producer: Event publizieren</em>
client.publishEvent("pubsub", "bestellungen", new BestellungAufgegeben("order-42"))
.block();
Code-Sprache: JavaScript (javascript)
<em>// Consumer: Event empfangen via REST-Endpunkt</em>
import io.dapr.Topic;
import org.springframework.web.bind.annotation.*;
@RestController
public class BestellungSubscriber {
@Topic(name = "bestellungen", pubsubName = "pubsub")
@PostMapping("/bestellungen")
public void onBestellung(@RequestBody CloudEvent<BestellungAufgegeben> event) {
BestellungAufgegeben data = event.getData();
System.out.println("Bestellung empfangen: " + data.bestellnummer());
}
}
Code-Sprache: JavaScript (javascript)
Dapr liefert Events im CloudEvents-Format — sprach- und plattformunabhängig. Der Consumer ist ein simpler REST-Endpunkt; Dapr managed Subscriptions, At-Least-Once-Delivery und Retries.
Secrets Management: Keine Secrets im Code
Map<String, String> secrets = client.getSecret("secretstore", "db-password").block();
String password = secrets.get("db-password");
<em>// App-Code enthält keine Credentials — sie kommen aus dem Sidecar</em>
Code-Sprache: JavaScript (javascript)
Dapr unterstützt AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, Kubernetes Secrets und HashiCorp Vault als Backends.
Komplette Beispiel-Anwendung
@SpringBootApplication
@RestController
public class OrderService {
public static void main(String[] args) {
SpringApplication.run(OrderService.class, args);
}
@Autowired private DaprClient daprClient;
@PostMapping("/orders")
public ResponseEntity<String> createOrder(@RequestBody NewOrderRequest req) {
<em>// 1. State speichern</em>
Bestellung order = new Bestellung(UUID.randomUUID().toString(),
req.product(), req.amount());
daprClient.saveState("statestore", order.id(), order).block();
<em>// 2. Payment-Service aufrufen</em>
DaprInvokeHttpClient paymentClient =
daprClient.invokeHttpClient("payment-service");
HttpRequest paymentReq = paymentClient.newRequestBuilder("pay")
.POST(DaprBodyPublishers.json(new ZahlungRequest(order.id(), order.amount())))
.build();
paymentClient.send(paymentReq, HttpResponse.BodyHandlers.ofString());
<em>// 3. Event publizieren</em>
daprClient.publishEvent("pubsub", "order-created",
new OrderCreatedEvent(order.id())).block();
return ResponseEntity.ok("Bestellung " + order.id() + " angelegt");
}
}
record Bestellung(String id, String product, double amount) {}
record NewOrderRequest(String product, double amount) {}
record ZahlungRequest(String orderId, double amount) {}
record OrderCreatedEvent(String orderId) {}
Code-Sprache: JavaScript (javascript)
Dapr im Kubernetes-Cluster
Im Kubernetes-Cluster wird der Dapr-Sidecar automatisch per Annotation injiziert:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "order-service"
dapr.io/app-port: "8080"
Code-Sprache: JavaScript (javascript)
Keine Container-Definition nötig — der Dapr-Sidecar-Container wird per Mutating Webhook angefügt.
Testcontainers für lokale Entwicklung
import io.dapr.testcontainers.DaprContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
class OrderServiceTest {
@Container
static DaprContainer dapr = new DaprContainer("daprio/daprd:1.18.0")
.withAppName("order-service")
.withAppPort(8080);
@Test
void testWithDapr() {
<em>// Dapr-API steht unter dapr.getHttpEndpoint() zur Verfügung</em>
}
}
Code-Sprache: JavaScript (javascript)
Wann lohnt sich Dapr?
Dapr ist kein Allheilmittel, aber in diesen Szenarien besonders wertvoll:
- Multi-Sprache/Multi-Team — einheitliche Infrastruktur-API für Java, Go, Python, JS
- Polyglotte Infrastruktur — Wechsel von Redis zu PostgreSQL → nur YAML ändern
- Sicherheitsanforderungen — mTLS, Secrets, kein Credential im App-Code
- On-Premise zu Cloud — gleicher Code, andere Komponenten-Konfiguration
Der Trade-off: Ein zusätzlicher Sidecar-Prozess pro Pod, und die Kommunikation läuft immer über localhost → Sidecar → Sidecar → localhost. Für latenzkritische Pfade kann dieser Hop relevant sein.
Fazit
Dapr ist ein Game-Changer für Infrastruktur-lastige Microservice-Landschaften. Es entkoppelt Business-Logik von Infrastruktur-Code und macht deine Services portabel zwischen Cloud-Providern und On-Premise. Das Java SDK 1.17.4 macht die Integration in Spring Boot fast unsichtbar.
Wenn dein nächstes Projekt mehr als zwei Services und mehr als einen Infrastruktur-Dienst (DB, Queue, Secrets) braucht: Gib Dapr einen Tag Testzeit — es könnte dir Monate Entwicklungsarbeit sparen.