Production-Grade Spring Boot APIs — Part 2: Clean Code Structure, Controllers & DTOs

Published: (January 17, 2026 at 02:52 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

What this post covers

  • Controller responsibility (API boundary, not logic)
  • Why @RestController is 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)

AnnotationPurpose
@ServiceBusiness logic
@RepositoryData access
@ControllerMVC layer
@RestControllerREST 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.

Back to Blog

Related posts

Read more »

Let's separate the separation

Introduction During the last days of 2025, our team lead took an extra day off and missed an important meeting. After a recent restructure, one colleague left...