
In un’applicazione Spring Boot, i DTO (Data Transfer Object) sono strumenti essenziali per gestire il flusso di dati in modo chiaro, sicuro e modulare. Spesso, nelle prime fasi di sviluppo, si tende a utilizzare direttamente le entità JPA nei controller per semplificare la gestione dei dati. Sebbene possa sembrare comodo, questo approccio porta a diversi problemi: si rischia di esporre informazioni sensibili, di creare accoppiamenti stretti tra i layer e di rendere difficile la manutenzione del codice. I DTO risolvono queste criticità separando la rappresentazione dei dati destinata all’API dalla struttura interna delle entità, creando un livello di astrazione che rende l’applicazione più robusta e scalabile.
Ruoli dei DTO: Request, Response e Comunicazione Interna
I DTO hanno molteplici ruoli all’interno di un’applicazione Spring Boot. Il primo e più noto riguarda le API, dove i DTO servono sia a ricevere i dati dalle richieste del client sia a definire i dati da restituire nelle risposte. Un DTO di richiesta può includere regole di validazione per garantire che i dati in ingresso siano coerenti e completi, mentre un DTO di risposta può filtrare informazioni riservate o riorganizzare i dati per renderli più leggibili e coerenti con le esigenze del client.
Tuttavia, i DTO sono altrettanto importanti per il passaggio di dati tra i vari elementi dell’applicazione. In un’architettura complessa, i servizi devono spesso scambiarsi informazioni senza legarsi direttamente alle entità JPA, che sono pensate principalmente per la persistenza. Utilizzando DTO interni, è possibile trasferire solo le informazioni necessarie, aggregare dati provenienti da più entità e ridurre i side-effect indesiderati. Questo approccio facilita la collaborazione tra moduli, migliora la manutenibilità e rende i servizi facilmente testabili. In pratica, i DTO diventano un linguaggio comune tra i componenti dell’applicazione, separando chiaramente responsabilità e riducendo il rischio di errori legati alla manipolazione diretta delle entità.
Creare un DTO in Spring Boot
Un DTO è fondamentalmente una semplice classe Java che contiene i campi necessari e, se richiesto, annotazioni di validazione. Ad esempio, per la creazione di un utente, un DTO potrebbe contenere nome e email con annotazioni come @NotNull
e @Email
per assicurare che i dati siano corretti. Il controller può utilizzare @Valid
per applicare automaticamente queste regole, intercettando eventuali errori prima che i dati raggiungano la logica di business.
public class UserRequestDTO {
@NotNull
@Size(min = 3, max = 50)
private String name;
@NotNull
@Email
private String email;
// getter e setter
}
public class UserResponseDTO {
private Long id;
private String name;
private String email;
// getter e setter
}
Gestire la conversione tra DTO ed Entity
Le entità JPA rimangono separate e contengono solo i dati necessari alla persistenza. La conversione tra DTO e entity può essere gestita manualmente, con metodi di mapping statici, oppure tramite librerie dedicate come MapStruct o ModelMapper, che automatizzano il processo e riducono il codice boilerplate. Questo permette di mantenere le entità indipendenti dalle regole di validazione e dalla struttura dei dati esposta alle API, garantendo un codice modulare e facilmente manutenibile.
public class UserMapper {
public static User toEntity(UserRequestDTO dto) {
User user = new User();
user.setName(dto.getName());
user.setEmail(dto.getEmail());
return user;
}
public static UserResponseDTO toDTO(User user) {
UserResponseDTO dto = new UserResponseDTO();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setEmail(user.getEmail());
return dto;
}
}
Utilizzare i DTO nel Controller
Nel controller, i DTO vengono utilizzati per ricevere le richieste e inviare le risposte. Il controller passa i DTO al servizio, che esegue la logica di business e restituisce entità che vengono poi convertite in DTO di risposta. In questo modo, ogni componente dell’applicazione mantiene responsabilità chiare: il controller gestisce la comunicazione con il client, il servizio gestisce la logica di business e il repository gestisce la persistenza.
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<UserResponseDTO> createUser(@Valid @RequestBody UserRequestDTO userDTO) {
User user = userService.createUser(UserMapper.toEntity(userDTO));
return ResponseEntity.ok(UserMapper.toDTO(user));
}
@GetMapping("/{id}")
public ResponseEntity<UserResponseDTO> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
return ResponseEntity.ok(UserMapper.toDTO(user));
}
}
Flusso dati Spring Boot con DTO
Client (Browser / App)
│
▼
DTO di Request
(UserRequestDTO con validazioni)
│
▼
Controller
(Riceve DTO, @Valid, chiama il servizio)
│
▼
Service Layer
(Applica logica di business, chiama repository)
│
▼
Entity JPA / Repository
(Salvataggio o recupero dati dal DB)
│
▼
Mapper / DTO Conversion
(Entity → UserResponseDTO / DTO interno)
│
▼
DTO di Response
(Filtra o aggrega dati da restituire)
│
▼
Client
Descrizione del flusso
-
Client: invia dati tramite API, ad esempio una richiesta POST per creare un utente.
-
DTO di Request: il controller riceve un DTO di richiesta (
UserRequestDTO
) che applica regole di validazione (@Valid, @NotNull, ecc.). -
Controller: riceve il DTO, verifica la validità dei dati e li passa al servizio.
-
Service Layer: esegue la logica di business. Può utilizzare DTO interni per passare solo i dati rilevanti tra servizi o trasformare più entità in un singolo DTO aggregato.
-
Repository / Entity: interagisce con il database tramite JPA. Le entità non vengono mai esposte direttamente al client.
-
Mapper / Conversione: il servizio converte le entità in DTO di risposta o DTO interni tramite mapper manuali o librerie come MapStruct.
-
DTO di Response: viene restituito al client filtrando e riorganizzando i dati.
I DTO all’interno dell’applicazione: comunicazione tra servizi
Oltre all’uso per le API, i DTO sono strumenti preziosi per trasferire dati tra servizi o componenti interni. In un’applicazione complessa, non sempre è necessario passare l’intera entità JPA da un servizio all’altro. I DTO interni consentono di spostare solo i dati rilevanti, di aggregare informazioni da più entità e di ridurre la possibilità di modifiche accidentali ai dati persistenti. Questo approccio favorisce la manutenibilità, facilita i test e migliora la leggibilità del codice.
public class UserStatsDTO {
private Long userId;
private int postsCount;
private int commentsCount;
// getter e setter
}
Un servizio può costruire un UserStatsDTO
aggregando informazioni da più repository e passarlo a un altro servizio, come quello delle notifiche, senza esporre direttamente l’entità User
completa. In questo modo, ogni servizio riceve solo i dati necessari, e la struttura interna delle entità resta isolata.
Vantaggi concreti dell’uso dei DTO
L’uso dei DTO migliora la sicurezza, perché permette di esporre solo i dati necessari, evitando di rivelare informazioni sensibili. Aumenta la manutenibilità, poiché modifiche interne al modello di dominio non impattano direttamente le API o altri servizi. Ottimizza le prestazioni, trasferendo solo i dati essenziali tra client e server o tra componenti interni, riducendo la complessità della serializzazione e deserializzazione. Inoltre, favorisce la chiarezza e modularità del codice, separando responsabilità e semplificando test e refactoring.
Conclusione
I DTO in Spring Boot sono strumenti indispensabili per costruire applicazioni robuste, sicure e scalabili. Separare entità, servizi e controller, utilizzare DTO per le richieste, le risposte e la comunicazione interna tra servizi consente di avere un flusso di dati chiaro, modulare e manutenibile. L’adozione dei DTO fin dalle prime fasi di sviluppo riduce errori, migliora la sicurezza e facilita la crescita del progetto nel tempo.