Spring MVC und REST APIs
Spring MVC ist das klassische Servlet-basierte Webframework für REST APIs, serverseitige Webanwendungen und Controller-basierte HTTP-Kommunikation.
Fachliche Einordnung
Der größte Teil klassischer Enterprise-APIs läuft weiterhin blockierend über Servlet-Container. MVC ist robust, verständlich, gut testbar und passt zu JPA/JDBC-Workloads.
Enterprise-Merksatz: Spring MVC ist das klassische Servlet-basierte Webframework für REST APIs, serverseitige Webanwendungen und Controller-basierte HTTP-Kommunikation.
Technische Darstellung
Kernkonzepte
- DispatcherServlet als Front Controller.
- @Controller und @RestController für Request Mapping.
- HandlerMethodArgumentResolver und HttpMessageConverter.
- Bean Validation für Eingabegrenzen.
- ExceptionHandler und ProblemDetail für konsistente Fehlerantworten.
Wann einsetzen?
- Du baust REST APIs mit relationaler Datenbank und synchronem Request/Response.
- Du migrierst JAX-RS oder Servlet APIs nach Spring.
- Du willst gut testbare Controller mit MockMvc.
Typische Fehler und Risiken
- Controller enthält Fachlogik statt nur HTTP-Adapterlogik.
- DTOs werden mit JPA Entities vermischt.
- Fehlerantworten sind uneinheitlich und schwer für Clients zu verarbeiten.
Legacy- und Modernisierungssicht
Servlets, Struts Actions oder JAX-RS Ressourcen werden zu dünnen MVC-Controllern, die Application Services aufrufen.
Ausführliches Beispiel
Das Beispiel zeigt bewusst nicht nur Annotationen, sondern auch die Verantwortung der Schicht. In echten Projekten sollte der technische Spring-Code an Adapter- oder Konfigurationsrändern bleiben, während die Fachlogik testbar und möglichst frameworkarm bleibt.
@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());
URI location = URI.create("/api/orders/" + id.value());
return ResponseEntity.created(location).body(new OrderResponse(id.value(), "ACCEPTED"));
}
}
@RestControllerAdvice
class ApiExceptionHandler {
@ExceptionHandler(DomainRuleViolation.class)
ProblemDetail handle(DomainRuleViolation ex) {
ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.UNPROCESSABLE_ENTITY);
problem.setTitle("Domain rule violated");
problem.setDetail(ex.getMessage());
return problem;
}
}
Checkliste für Reviews
- Ist die Verantwortung des Bausteins klar: Framework steuert Lebenszyklus, Library wird gezielt benutzt?
- Ist die Fachlogik außerhalb von Controller, Listener, Repository-Implementierung oder Konfiguration?
- Sind Fehlerfälle, Timeouts, Security, Monitoring und Tests sichtbar modelliert?
- Gibt es klare Grenzen zwischen DTO, Domäne, Persistence und Infrastruktur?