Enterprise-grade Spring Boot 3.5.x/4.x development guide. Use when building Spring Boot APIs, microservices, or enterprise Java applications. Covers Java Records as DTOs, Virtual Threads, ProblemDetail (RFC 7807), RestClient, structured logging, Testcontainers, observability, sealed exception hierarchies, and GraalVM native. Triggers on: Spring Boot, Spring MVC, REST APIs in Java, microservices in Java, enterprise Java development.
Production-ready Spring Boot guide. Updated for Spring Boot 3.5.x / 4.0.x, Java 17+ (optimized for 21+).
spring.threads.virtual.enabled=true)spring-boot-starter-parent for dependency management@RequiredArgsConstructor@Data classes)@RestControllerAdvice@Valid + Bean Validation on record components{} placeholders + structured loggingspring.threads.virtual.enabled=truecontroller/ → HTTP routing only (thin)
↓
service/ → Business logic, caching, validation
↓
repository/ → Spring Data JPA
↓
entity/ → JPA domain models
Each layer depends only on layers below. Controllers never access repositories directly. See architecture.md for layer-based vs feature-based patterns.
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository repository;
private final ProductMapper mapper;
}
Records replace @Data Lombok classes: immutable, concise, thread-safe.
// Request (with validation)
public record CreateProductRequest(
@NotBlank @Size(min = 3, max = 100) String name,
@NotNull @DecimalMin("0.01") BigDecimal price,
@Size(max = 500) String description
) {}
// Response
public record ProductResponse(
Long id, String name, BigDecimal price,
String description, Instant createdAt
) {}
@Component
public class ProductMapper {
public ProductResponse toResponse(Product entity) {
return new ProductResponse(entity.getId(), entity.getName(),
entity.getPrice(), entity.getDescription(), entity.getCreatedAt());
}
public Product toEntity(CreateProductRequest req) {
return Product.builder()
.name(req.name()).price(req.price()).description(req.description()).build();
}
}
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService service;
@PostMapping
public ResponseEntity<ProductResponse> create(
@Valid @RequestBody CreateProductRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(request));
}
@GetMapping("/{id}")
public ResponseEntity<ProductResponse> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@GetMapping
public ResponseEntity<Page<ProductResponse>> list(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return ResponseEntity.ok(service.getAll(PageRequest.of(page, size)));
}
}
See ProductController.java and ProductService.java for complete examples.