Diese Seite bleibt auch ohne JavaScript lesbar. Suche und Buttons sind Zusatzkomfort.
Spring Web MVC und REST APIs
Controller als Adapter, DTOs als Vertrag, Services als Use-Case-Grenze.
1. Controller sind Adapter, keine Fachschicht
Ein sauberer REST-Controller nimmt HTTP entgegen, validiert Protokoll- und DTO-Regeln, ruft einen Use Case auf und übersetzt das Ergebnis in HTTP. Er sollte keine Transaktionslogik, keine SQL-Details und keine komplexen fachlichen Entscheidungen enthalten.
DTOs sind nicht dasselbe wie Entities. Entities direkt als REST-Vertrag zu veröffentlichen führt häufig zu Lazy-Loading-Problemen, ungewollten Feldern, Versionskopplung und Security-Lecks.
@RestController
@RequestMapping("/api/orders")
class OrderController {
private final PlaceOrderUseCase placeOrder;
OrderController(PlaceOrderUseCase placeOrder) {
this.placeOrder = placeOrder;
}
@PostMapping
ResponseEntity<OrderResponse> place(@Valid @RequestBody PlaceOrderRequest request) {
OrderId id = placeOrder.place(request.toCommand());
return ResponseEntity.created(URI.create("/api/orders/" + id.value()))
.body(new OrderResponse(id.value(), "ACCEPTED"));
}
}
record PlaceOrderRequest(@NotBlank String customerId, @NotEmpty List<OrderLineDto> lines) {
PlaceOrder toCommand() {
return new PlaceOrder(customerId, lines);
}
}
2. Fehlerdesign mit Problem Details
Enterprise-APIs brauchen ein vorhersehbares Fehlerformat. Ein fachlicher Konflikt sollte nicht als zufällige 500-Exception enden. Validierungsfehler, Berechtigungsfehler, nicht gefundene Ressourcen und fachliche Konflikte bekommen eigene Statuscodes und strukturierte Antworten.
@RestControllerAdvice
class ApiExceptionHandler {
@ExceptionHandler(OrderLimitExceededException.class)
ResponseEntity<ProblemDetail> orderLimit(OrderLimitExceededException ex) {
ProblemDetail detail = ProblemDetail.forStatus(HttpStatus.CONFLICT);
detail.setTitle("Order limit exceeded");
detail.setDetail(ex.getMessage());
detail.setProperty("errorCode", "ORDER_LIMIT_EXCEEDED");
return ResponseEntity.status(HttpStatus.CONFLICT).body(detail);
}
}
3. Versionierung und API-Verträge
Versionierung sollte nicht reflexartig über /v1, /v2 passieren. Wichtig ist, welche Änderung kompatibel ist: neue optionale Felder sind meist kompatibel, entfernte oder semantisch geänderte Felder nicht. Für Enterprise-Teams sind OpenAPI, Contract Tests und Deprecation-Regeln wichtiger als eine hübsche URL.
Enterprise-Prüffragen
- Keine Entity direkt als REST Response?
- Controller dünn und Use-Case-orientiert?
- Zentrales Fehlerformat vorhanden?
- OpenAPI/Contract Tests für kritische APIs?