07 Data Transfer Objects
Data Transfer Objects (DTOs) are objects that carry data between processes. They are used to encapsulate data and send it over the network or between different layers of an application. Their main use case for APIs is to define the structure of the body of a request and response.
Purpose
As we do not want to expose the entity objects direclty to the client, we use DTOs to transfer data. DTOs allow us to control what data is sent to the client and how it is structured. They also help to decouple the internal representation of data from the external representation.
The also prevent problems with open-session-in-view and lazy loading issues. Where the entity object wants to load data from the database, but the relevant session is already closed.
Mapping
The service layer needs to map the entity objects to DTOs and vice versa. This mapping can be done manually or using libraries like MapStruct, ModelMapper, or Dozer.
Manual Mapping
Manual mapping involves writing code to copy the fields from an entity object to a DTO and vice versa. This approach gives you full control over the mapping logic but can be tedious and error-prone. An example of manual mapping could look like this:
public class UserMapper {
public static UserDto toDto(User user) {
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setName(user.getName());
return userDto;
}
public static User toEntity(UserDto userDto) {
User user = new User();
user.setId(userDto.getId());
user.setName(userDto.getName());
return user;
}
}
MapStruct
MapStruct is a code generator that simplifies the mapping process by generating the mapping code at compile time. It uses annotations to define the mapping between objects. An example of using MapStruct to map a User entity to a UserDto could look like this:
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDto toDto(User user);
User toEntity(UserDto userDto);
}