MODEL, DTO, CONVERTER E MAPPER
Il Data Transfer Object - meglio noto come DTO - è un design pattern utilizzato per trasferire i dati tra le diverse parti di un'applicazione software. In particolare i dto sono usati per recuperare le informazioni da una base dati ed esporre le stesse ad un layer di presentazione.
Di fatto, i dto definiscono indirettamente una fase di assemblaggio nella quale tutti i dati da inviare ad una view sono prelevati e marshalizzati.
La prima soluzione che affrontiamo nell'articolo è quella che prevede l'utilizzo dei Converter, ossia di classi apposite che attraverso l'utilizzo dei generics di Java consentono per l'appunto questa operazione di conversione.
L'idea è quella di creare un'interfaccia del tipo:
public interface BaseConverter<T,K> {
T toModel(K dto);
K toDto(T model);
List<K> toDtos(List<T> models);
}
A questo punto, supponendo di avere un'entità Persona ed il relativo DTO, è possibile implementare il converter base come segue:
@Component
public class PersonConverter implements BaseConverter<Person, PersonDTO> {
@Override
public Person toModel(PersonDTO dto) {
....
}
@Override
public PersonDTO toDto(Person model) {
....
}
@Override
public List<PersonDTO> toDtos(List<Person> models) {
....
}
}
Il vantaggio di questa soluzione è quello di avere maggior controllo sul codice; lo svantaggio è che spesso questi metodi risultano molto verbosi in quanto contenenti tutti i set degli attributi degli oggetti.
Una soluzione alternativa prevede l'utilizzo di mapstruct. Questa libreria, attraverso l'utilizzo delle reflection API, effettua un mapping automatico tra entity e dto degli attributi con lo stesso nome.
Supponendo, nel nostro caso, che Person e PersonDTO abbiano attributi uguali si ha un'interfaccia del tipo:
@Mapper
public interface PersonMapper {
Person toModel(PersonDTO personDTO);
PersonDTO toDto(Person person);
}
Una volta effettuato il mvn clean install, l'implementazione di questa interfaccia verrà generata automaticamente all'interno della cartella target. Tuttavia a noi basterà iniettare l'interfaccia all'interno della classe in cui vogliamo effettuare un'operazione di mapping ed il gioco è fatto.
Invece, qualora alcune proprietà siano diverse tra model e dto, è possibile indicare a mapstruct l'attributo sorgente e quello di destinazione:
@Mapper
public interface PersonMapper {
@Mapping(source="name", target="firstname")
Person toModel(PersonDTO personDTO);
}
Nell'esempio di cui sopra, abbiamo trasferito la proprietà name del dto PersonDTO nell'attributo firstname dell'entity Person. Ovviamente è possibile aggiungere tutti i mapping che ci occorrono nel caso di difformità di naming degli attributi.
Infine, la libreria mapstruct consente anche di effettuare un minimo di logica sui campi. Supponiamo ad esempio di voler esporre nel dto un attributo fullName che sia la concatenazione tra il name ed il surname. Ciò è possibile nel seguente modo:
@Mapper
public interface PersonMapper {
@Mapping(source="personDTO", target="fullName", qualifiedByName="getFullName")
Person toModel(PersonDTO personDTO);
@Named("getFullName")
static String getFullName(PersonDTO personDTO) {
return personDTO.getName().concat(" " + personDTO.getSurname());
}
}
Comments
Post a Comment