Sobre DTOs e programação estruturada
Com a minha própria autorização, replico aqui um post do meu blog:
Os DTOs (Data Transfer Object) tem como principal uso a transferência de dados entre diferentes camadas de uma aplicação. A principal característica desse tipo de objeto é falta de comportamento.
O problema: DTOs as vezes são usados além do seu propósito inicial, o que leva o desenvolvedor à programação estruturada. Considere por exemplo os seguintes exemplos, primeiro em Java:
public class User {
private String login;
private String passwortHash;
[getters e setters]
}
public class UserUtil {
public static boolean checkLogin(User user) {
...
}
}
Agora em C:
struct User {
char* login;
char* passwordHash;
};
int checkLogin(User user) {
...
}
Nota: não compilei os códigos, mas são suficientes para objetivos didáticos.
Perceberam a semelhança? Apesar da keyword “class” estar presente na versão Java, isso NÃO é orientação a objetos.
DTOs têm usos justificados, porém muitas vezes acabam se espalhando pela aplicação. A consequência dessa difusão é que o comportamento que utiliza os dados de um DTO tem dois “destinos” principais:
- Uma “classe” com vários métodos estáticos;
- no pior caso, o comportamento é duplicado em cada lugar que é usado.
A duplicação do comportamento pode ser bem problemática. Muitas vezes um mesmo comportamento está replicado com pequenas variações (e.g. uma comparação de string case-sensitive e outra case-insensitive). Esse acoplamento com o DTO dentro da aplicação também dificulta a mudança do mesmo, pois dependendo da escala da aplicação os pontos de mudança são muitos, e como não há uma centralização dos comportamentos, refactorings automatizados estão fora de questão.
Outro problema que ocorre nos dois casos é que esse estilo de programação dificulta a criação de testes da aplicação. Ao invés de criar um mock do objeto (estendendo o objeto/interface ou com algum framework), é necessário criar um objeto real com os dados necessários para o comportamento desejado. Com isso, o teste que deveria ter um ponto da aplicação sendo testado possui uma dependência com o comportamento associado ao DTO (ou seja, os testes falham se o “comportamento do DTO” for alterado).
Muitas vezes os DTOs são uma dependência externa do projeto (e.g. equipes distribuídas, cada uma trabalhando em um módulo da aplicação). Neste caso, se não for possível colocar o comportamento junto com os dados, é interessante ter uma classe que encapsule o DTO, fornecendo os comportamentos do mesmo. Desta forma evita-se que toda a aplicação conheça o funcionamento do DTO (flags ou outros atributos internos), fornece um ponto centralizado com o comportamento e melhora muito a legibilidade do código, pois os dados estão juntos com o comportamento associado.
Concordo totalmente com o Besen. A utilização de DTOs implica na separação de informações e semântica, e isto incorre em todos os problemas relatados acima.
Há casos em que essa separação é conveniente, como nos casos onde vários módulos realmente dão semântica diferente às mesmas entidades. Por exemplo: O módulo de cadastro de usuários irá interagir com os usuários de modo diferente do que o módulo de autenticação de usuários. Mas mesmo nestes casos, fornecer uma classe que ofereça o comportamento ao módulo e encapsule as informações, como sugerido pelo Besen, traz de volta as vantagens da O.O. sem acoplar os diversos módulos.
Besen, parabéns pelo ótimo post!