Sempre que um desenvolvedor pergunta "qual é o sentido de fazer isso?", O que eles realmente querem dizer é "não vejo nenhum caso de uso em que isso forneça um benefício". Para esse fim, deixe-me mostrar alguns exemplos.
Todos os exemplos serão baseados neste modelo de dados simples:
Uma Person
entidade possui cinco propriedades:Id, FirstName, LastName, Age, CityId
E você pode supor que o aplicativo use esses dados de várias maneiras (relatórios, formulários, pop-ups, ...).
O aplicativo inteiro já existe. Tudo o que eu mencionei é uma alteração na base de código existente. Isso é importante lembrar.
Exemplo 1 - Alterando a estrutura de dados subjacente - sem DTO
Os requisitos foram alterados. A idade da pessoa precisa ser recuperada dinamicamente do banco de dados do governo (vamos assumir com base no nome e sobrenome).
Como você não precisa mais armazenar o Age
valor localmente, ele precisa ser removido da Person
entidade. É importante perceber aqui que a entidade representa os dados do banco de dados e nada mais. Se não estiver no banco de dados, não está na entidade.
Quando você recupera a idade do serviço da web do governo, ele será armazenado em um objeto (ou int) diferente.
Mas seu frontend ainda exibe uma idade. Todas as visualizações foram configuradas para usar a Person.Age
propriedade, que agora não existe mais. Um problema se apresenta: todas as visualizações que se referem à Age
pessoa precisam ser corrigidas .
Exemplo 2 - Alterando a estrutura de dados subjacente - Com DTO
No sistema antigo, também há PersonDTO
entidade com as mesmas cinco propriedades: Id, FirstName, LastName, Age, CityId
. Após recuperar a Person
, a camada de serviço a converte em a PersonDTO
e depois a devolve.
Mas agora, os requisitos mudaram. A idade da pessoa precisa ser recuperada dinamicamente do banco de dados do governo (vamos assumir com base no nome e sobrenome).
Como você não precisa mais armazenar o Age
valor localmente, ele precisa ser removido da Person
entidade. É importante perceber aqui que a entidade representa os dados do banco de dados e nada mais. Se não estiver no banco de dados, não está na entidade.
No entanto, como você tem um intermediário PersonDTO
, é importante ver que essa classe pode manter a Age
propriedade. A camada de serviço buscará Person
, converterá em a PersonDTO
e também buscará a idade da pessoa no serviço web do governo, armazenará esse valor PersonDTO.Age
e passará esse objeto.
A parte importante aqui é que quem usa a camada de serviço não vê diferença entre o antigo e o novo sistema . Isso inclui seu frontend. No sistema antigo, ele recebeu um PersonDTO
objeto completo . E no novo sistema, ele ainda recebe um PersonDTO
objeto completo . As visualizações não precisam ser atualizadas .
É isso que queremos dizer quando usamos a frase separação de preocupações : Existem duas preocupações diferentes (armazenar os dados no banco de dados, apresentar os dados ao front-end) e eles precisam de um tipo de dados diferente para cada um. Mesmo que esses dois tipos de dados contenham os mesmos dados agora, isso pode mudar no futuro.
No exemplo dado, Age
há uma diferença entre os dois tipos de dados: Person
(a entidade do banco de dados) não precisa de um Age
, mas PersonDTO
(o tipo de dados de front-end) precisa.
Ao separar as preocupações (= criar tipos de dados separados) desde o início, a base de código é muito mais resistente às alterações feitas no modelo de dados.
Você pode argumentar que ter um objeto DTO, quando uma nova coluna é adicionada ao banco de dados, significa que você deve fazer um trabalho duplo, adicionando a propriedade na entidade e no DTO. Isso é tecnicamente correto. Exige um pouco de esforço extra para manter duas classes em vez de uma.
No entanto, você precisa comparar o esforço necessário. Quando uma ou mais novas colunas são adicionadas, copiar / colar algumas propriedades não leva tanto tempo. Quando o modelo de dados muda estruturalmente, é necessário alterar o frontend, possivelmente de maneiras que apenas causam bugs no tempo de execução (e não no tempo de compilação), exige muito mais esforço e exige que os desenvolvedores procurem bugs.
Eu poderia lhe dar mais exemplos, mas o princípio sempre será o mesmo.
Resumir
- Responsabilidades (preocupações) separadas precisam trabalhar separadamente uma da outra. Eles não devem compartilhar recursos, como classes de dados (por exemplo
Person
)
- Só porque uma entidade e seu DTO têm as mesmas propriedades, não significa que você precisa mesclá-las na mesma entidade. Não corte cantos.
- Como um exemplo mais flagrante, digamos que nosso banco de dados contenha países, músicas e pessoas. Todas essas entidades têm a
Name
. Mas apenas porque todos eles têm uma Name
propriedade, não significa que devemos fazê-los herdar de uma EntityWithName
classe base compartilhada . As diferentes Name
propriedades não têm relação significativa.
- Se alguma das propriedades mudar (por exemplo, uma música
Name
é renomeada Title
ou uma pessoa recebe um FirstName
e LastName
), elas terão que se esforçar mais para desfazer a herança que você nem precisava em primeiro lugar .
- Embora não seja tão flagrante, seu argumento de que você não precisa de um DTO quando possui uma entidade é o mesmo. Você está olhando para o agora , mas não está se preparando para mudanças futuras. SE a entidade e o DTO forem exatamente iguais, e se você puder garantir que nunca haverá alterações no modelo de dados; então você está certo de que pode omitir o DTO. Mas o importante é que você nunca pode garantir que o modelo de dados nunca será alterado.
- As boas práticas nem sempre são recompensadas imediatamente. Pode começar a valer a pena no futuro, quando você precisar revisitar um aplicativo antigo.
- A principal causa de morte das bases de código existentes é deixar a qualidade do código diminuir, dificultando continuamente a manutenção da base de código, até que ela se torne uma bagunça inútil de código espaguete que é inatingível.
- As boas práticas, como a implementação de uma separação de preocupações do início, visam evitar essa ladeira escorregadia de má manutenção, a fim de manter a base de código em manutenção pelo maior tempo possível.
Como regra geral para considerar a separação de preocupações, pense da seguinte maneira:
Suponha que todas as preocupações (a interface do usuário, o banco de dados, a lógica) sejam tratadas por uma pessoa diferente em um local diferente. Eles só podem se comunicar por email.
Em uma base de código bem separada, uma alteração em uma preocupação específica precisará ser tratada apenas por uma pessoa:
- Alterar a interface do usuário envolve apenas o desenvolvedor da interface do usuário.
- Alterar o método de armazenamento de dados envolve apenas o desenvolvedor do banco de dados.
- Alterar a lógica de negócios envolve apenas o desenvolvedor de negócios.
Se todos esses desenvolvedores usassem a mesma Person
entidade e uma pequena alteração fosse feita na entidade, todos precisariam estar envolvidos no processo.
Mas, usando classes de dados separadas para cada camada, esse problema não é tão prevalente:
- Desde que o desenvolvedor do banco de dados possa retornar um
PersonDTO
objeto válido , o desenvolvedor de negócios e a interface do usuário não se importam se ele mudou a maneira como os dados são armazenados / recuperados.
- Desde que o desenvolvedor comercial armazene os dados no banco de dados e forneça os dados necessários ao front-end, os desenvolvedores do banco de dados e da interface do usuário não se importam se ele decidiu refazer suas regras de negócios.
- Desde que a interface do usuário possa ser projetada com base no `PersonViewModel, o desenvolvedor da UI poderá criar a interface da maneira que desejar. Os desenvolvedores de banco de dados e negócios não se importam com o que é feito, pois não os afetam.
A frase-chave aqui é que isso não os afeta . A implementação de uma boa separação de preocupações busca minimizar a afetação (e, portanto, a necessidade de envolver) outras partes.
Obviamente, algumas mudanças importantes não podem evitar a inclusão de mais de uma pessoa, por exemplo, quando uma entidade totalmente nova é adicionada ao banco de dados. Mas não subestime a quantidade de pequenas alterações que você precisa fazer durante a vida útil de um aplicativo. As principais mudanças são uma minoria numérica.
What's the benefit of these conversions?
dissociar o modelo de dados de persistência do modelo de dados (representação) oferecido aos consumidores. Os benefícios da dissociação foram amplamente discutidos no SE. No entanto, o objetivo dos DTOs é reunir em uma única resposta quantas informações forem necessárias para os clientes salvarem chamadas no servidor. O que torna a comunicação cliente-servidor mais suave.