Do DTO ao ViewModel: Estratégias de Mapeamento entre Camadas em C#
Introdução aos Conceitos de DTO e ViewModel
No desenvolvimento de aplicações, especialmente em arquitetura em camadas, dois conceitos fundamentais surgem: Data Transfer Object (DTO) e ViewModel. Ambos desempenham papéis cruciais na separação de preocupações e na manutenção da integridade dos dados. Um DTO é utilizado para transferir dados entre processos, enquanto um ViewModel encapsula os dados necessários para a camada de apresentação, frequentemente incluindo lógica específica de exibição.
O Papel do DTO
Os DTOs são essenciais para otimizar a comunicação entre a camada de persistência e a camada de apresentação (VIEIRA et al., 2024). Eles permitem que você transfira apenas os dados necessários, minimizando a sobrecarga de rede e melhorando o desempenho da aplicação. Um exemplo simples de DTO em C# pode ser visto abaixo:
public class UserDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Neste exemplo, o UserDTO
contém apenas as propriedades que queremos transferir, sem incluir lógica de negócio ou outros detalhes que não são necessários para a camada de apresentação. Isso facilita a deserialização e a serialização de dados entre o cliente e o servidor, garantindo que apenas as informações necessárias sejam transmitidas.
O Papel do ViewModel
O ViewModel, por outro lado, é projetado para atender às necessidades específicas da interface do usuário. Ele pode incluir propriedades que não estão diretamente relacionadas aos dados do modelo, mas que são necessárias para a exibição. Por exemplo:
using System.ComponentModel.DataAnnotations;
public class UserViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "O nome é obrigatório.")]
[StringLength(100, ErrorMessage = "O nome deve ter no máximo 100 caracteres.")]
public string Name { get; set; }
[Required(ErrorMessage = "O e-mail é obrigatório.")]
[EmailAddress(ErrorMessage = "Formato de e-mail inválido.")]
public string Email { get; set; }
[Display(Name = "Data de Registro")]
[DataType(DataType.Date)]
public DateTime RegisteredAt { get; set; }
[Display(Name = "E-mail Verificado")]
public bool IsEmailVerified { get; set; }
}
A propriedade IsEmailVerified
pode ser calculada a partir do DTO, mas não é parte dos dados transferidos do banco de dados. Isso demonstra como o ViewModel pode ser adaptado para atender às necessidades específicas da interface, incluindo informações adicionais que não estão presentes no modelo de dados.
Estratégias de Mapeamento entre DTO e ViewModel
O mapeamento entre DTOs e ViewModels pode ser feito de várias maneiras. A seguir, exploraremos algumas das estratégias mais comuns, incluindo o uso de bibliotecas de mapeamento e implementações manuais.
Uso de AutoMapper para Mapeamento
Uma das bibliotecas mais populares para mapeamento em C# é o AutoMapper. Ele permite que você defina mapeamentos entre tipos de forma simples e direta (BOGARD, 2025). Veja como configurar e usar o AutoMapper:
using AutoMapper;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<UserDTO, UserViewModel>()
.ForMember(dest => dest.IsEmailVerified, opt => opt.MapFrom(src =>
!string.IsNullOrEmpty(src.Email)));
}
}
// Configuração do AutoMapper
var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingProfile>());
var mapper = config.CreateMapper();
// Uso do AutoMapper
UserDTO userDto = new UserDTO { Id = 1, Name = "John Doe", Email = "john@example.com" };
UserViewModel userViewModel = mapper.Map<UserViewModel>(userDto);
É importante observar que as versões mais recentes do AutoMapper vão adotar um modelo de licenciamento que inclui planos pagos (BOGARD, 2025). Embora isso não afete a funcionalidade principal da biblioteca, pode representar uma limitação para equipes ou projetos com restrições orçamentárias. Nessas situações, pode ser necessário optar por versões anteriores, ainda gratuitas, ou considerar alternativas como mapeamento manual ou outras bibliotecas open source.
Mesmo assim, o AutoMapper continua sendo uma solução extremamente eficiente e produtiva para cenários em que o mapeamento entre objetos é frequente, ajudando a reduzir código repetitivo e erros humanos ao transferir dados entre camadas.
Mapeamento Manual
Embora o uso de bibliotecas como o AutoMapper seja benéfico, em alguns casos, o mapeamento manual pode ser preferível, especialmente quando você precisa de controle total sobre o processo de mapeamento. Veja um exemplo de mapeamento manual:
public UserViewModel MapToViewModel(UserDTO userDto)
{
return new UserViewModel
{
Id = userDto.Id,
Name = userDto.Name,
Email = userDto.Email,
IsEmailVerified = !string.IsNullOrEmpty(userDto.Email)
};
}
// Uso do mapeamento manual
UserDTO userDto = new UserDTO { Id = 1, Name = "Jane Smith", Email = "jane@example.com" };
UserViewModel userViewModel = MapToViewModel(userDto);
O mapeamento manual oferece clareza e flexibilidade, permitindo que você implemente lógica personalizada conforme necessário. Isso é especialmente útil em cenários onde a lógica de mapeamento pode mudar com frequência ou onde é necessário realizar transformações complexas nos dados.
Adoção e Testes Unitários
Ao escolher uma estratégia de mapeamento, é importante considerar o desempenho e a manutenção do código. O AutoMapper é eficiente para mapeamentos simples e repetitivos, mas pode introduzir complexidade em cenários mais avançados e também um custo a mais com o seu licenciamento. O mapeamento manual, por outro lado, pode ser mais verboso, mas oferece controle total sobre o que está sendo mapeado e como. É crucial avaliar o contexto da aplicação e a frequência das alterações nos modelos para decidir qual abordagem adotar.
Testando o Mapeamento
Abaixo um código para garantir que o mapeamento esteja funcionando como esperado. Abaixo um exemplo do xUnit:
using Xunit;
public class MappingTests
{
private readonly IMapper _mapper;
public MappingTests()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingProfile>());
_mapper = config.CreateMapper();
}
[Fact]
public void Should_Map_UserDTO_To_UserViewModel()
{
// Arrange
UserDTO userDto = new UserDTO { Id = 1, Name = "Alice", Email = "alice@example.com" };
// Act
UserViewModel userViewModel = _mapper.Map<UserViewModel>(userDto);
// Assert
Assert.Equal(userDto.Id, userViewModel.Id);
Assert.Equal(userDto.Name, userViewModel.Name);
Assert.Equal(userDto.Email, userViewModel.Email);
Assert.True(userViewModel.IsEmailVerified);
}
}
Os testes garantem que as alterações no mapeamento não quebrem a funcionalidade existente, proporcionando segurança e confiança nas mudanças do código. A inclusão de testes unitários é uma prática recomendada que deve ser parte integrante do ciclo de desenvolvimento, especialmente em aplicações complexas (MICROSOFT, 2025).
Conclusão
O mapeamento entre DTOs e ViewModels é um aspecto importante no desenvolvimento de aplicações em C#. Compreender as diferenças entre esses conceitos e as estratégias de mapeamento disponíveis pode ajudar os desenvolvedores a criar aplicações mais eficientes e manuteníveis. Seja utilizando uma biblioteca como o AutoMapper ou optando por mapeamento manual, o importante é sempre garantir que os dados sejam transferidos de maneira eficaz e que suas necessidades de apresentação sejam atendidas. Ao implementar testes adequados, você pode ter certeza de que suas implementações permanecem robustas ao longo do tempo.
Referências
-
BOGARD, Jimmy. AutoMapper Documentation. Disponível em: https://automapper.org/. Acesso em: 06 abr. 2025.
-
MICROSOFT, Unit Testing in .NET Core. Disponível em: https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices. Acesso em: 06 abr. 2025.
-
VIEIRA, Mateus Elias et al. Automação da Equoterapia por meio de Web Services. 2024.
-
BOGARD, Jimmy. AutoMapper and MediatR going commercial. 2023. Disponível em: https://www.jimmybogard.com/automapper-and-mediatr-going-commercial/. Acesso em: 06 abr. 2025.