Production-Grade Spring Boot APIs — Part 2: Clean Code Structure, Controllers & DTOs
Source: Dev.to
What this post covers
- Controller responsibility (API boundary, not logic)
- Why
@RestControlleris more than “return JSON” - Constructor injection (and why it’s mandatory)
- Spring stereotype annotations and why semantics matter
- Entity vs DTO (and why exposing entities is dangerous)
- Lombok in production systems
- Why ModelMapper keeps layers clean
Controller layer (API boundary)
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
}
What @RestController really means
- Registers the class as an HTTP request handler
- Returns JSON (not views)
- Integrated with
DispatcherServlet
This class defines API contracts, not business logic.
Constructor injection (non‑negotiable in production)
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
Why constructor injection:
- Dependencies are explicit
- Objects are immutable
- Easy to test
- Fails fast during startup (the application should not start if a dependency is missing)
Stereotype annotations (semantics matter)
| Annotation | Purpose |
|---|---|
@Service | Business logic |
@Repository | Data access |
@Controller | MVC layer |
@RestController | REST APIs |
All extend @Component, but they add meaning, readability, and layer‑specific behavior.
Entity vs DTO (never expose entities)
Entity
- Maps to database tables
- Used only for persistence
DTO
- Used for API communication
- Protects internal structure
Entities change with DB needs; DTOs change with API needs.
Lombok (production sanity)
Without Lombok:
- Missing setters → JSON fields become
null - Missing getters → empty responses
- Huge boilerplate
With Lombok:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Order {
private Long id;
private String description;
// other fields...
}
- Generates getters / setters, constructors, builders, etc.
- Keeps entities clean
Lombok is practically required in large Spring Boot codebases.
ModelMapper (layer separation)
- Converts Entity ⇄ DTO
- Keeps controllers clean
- Prevents leaking persistence models
Persistence means data survives beyond application runtime (database storage).
This is Part 2 of a 3‑part series on Production‑Grade Spring Boot API Design.
👉 Part 3 covers consistent API responses, BaseResponse, ResponseEntity, and global exception handling.