Use of DTO and Viewmodel in ASP.Net MVC project

Asked

Viewed 10,265 times

16

I have an ASP NET MVC 4 project with the following projects:

  • Domain
  • Repository
  • Contracts (interfaces)
  • DTO’s
  • And the web project

The web project "sees" only the repository project and it is responsible for executing the business rules. This project always returns DTO’s to the web layer and in the web layer (controllers) I turn the DTO into a viewmodel and return the viewmodel to the view.

I’m using DTO’s because in most queries I don’t need all the data from the entities, so I don’t need to expose my entire entity to the view (and I also think it’s not a good practice).

The problem I am seeing is that it is redundant to return a DTO to my web layer and in the web layer turn the DTO into viewmodel. Below is an example:

Suppose the repository has a method that returns user data (login and email) by id:

Return DTO of the method:

public class UsuarioDto{
   public string Login {get; set;}
   public string Email {get set;}
}

Method in the repository that returns the user to the web layer

public class UsuarioRepositorio : IUsuarioRepository
{
   public UsuarioDto GetUsuario(int usuarioId){
      using(var context = new dbContext()) //instancia do contexto do entity
      {
          return context.Usuario.Select(x => new UsuarioDto{
                                                 Login = x.Login,
                                                 Email = x.Email
                                              }
                                       ).FirstOrDefault(x.id == usuarioId);
      }
   }
}

At the point below I think I’m being redundant when turning DTO into viewmodel

Viewmodel representing the user (inside the Models folder)

public class UsuarioViewModel{
   public string Login {get; set;}
   public string Email {get set;}
}

Controller

public class HomeController : Controller 
{ 
    public ActionResult User(int usuarioId)
    {
       UsuarioRepositorio usuarioRepositorio = new UsuarioRepositorio();
       var usuario = usuarioRepositorio.GetUsuario(usuarioId)
                                       .Select(x => new UsuarioViewModel{
                                                        Login = x.Login,
                                                        Email = x.Email
                                                    }
                                              );
      return View(usuario)
    }
}

Is there a way for me to optimize this transformation from DTO to Viewmodel? The impression I have is that I am duplicating code, because as far as I know, the DTO serves to traffic data between layers and the viewmodel is used to expose the data to the view.

2 answers

13


Well, Tiago, for what I understood its biggest problem is the work in transforming Model in DTO, then DTO in Viewmodel, Viewmodel in DTO, then DTO in Model.

With Automapper, some of the work would be done and you would have fewer problems, but before simply using a solution of the kind, you can rethink your model as a whole. There are several design standards to suit your similar needs, of course no standard is perfect. First I think you should think about which points are most relevant to you: development speed, decoupling, maintainability, etc.

There is no perfect standard when defining an architecture, we always have to give up some things to achieve others, there is no efficient maintenance without testing, decoupling without separation of layers and responsibilities, etc.

Why use DTO (Data Transfer Object)? As the name says, data transfer object, when you have a system where you need to perform several queries and bring complete entities in separate queries becomes a problem, it is time to use a DTO.

From what I’ve said you’re seeing how a problem repeating this code of transforming one object into another is the opposite of using the pattern on uncomplicated screens, a simple example:

Screen Creation of User:

DTO:

public Guid Id {get;set;}
public string Nome {get;set;}
public string Cpf {get;set;}
public string Telefone {get;set;}
public string Email {get;set;}
public string Senha {get;set;}

ENTITY:

public Guid Id {get;set;}
public string Nome {get;set;}
public string Cpf {get;set;}
public string Telefone {get;set;}
public string Email {get;set;}
public string Senha {get;set;}

On your screen you will have to perform validations, your Viewmodel can not be equal your DTO, need the user Confirm E-mail and Confirm Password, may need to send various other information to your screen.

Now imagine if tomorrow you realize it’s better to separate the Entity User in Pessoa Física and Credential, for whatever reason. Your DTO moves the data of the two entities between the layers, continues using a single screen, no need to change your application, only your data access layer.

With some tools like I mentioned Automapper you can make your job of transforming one entity into another easy, with just one line of code, and you can take advantage of that pattern.

What you need to cherish always is the separation of responsibilities and simplicity, not always less code is the simplest, not always the most complex is the best, ponder your needs. Basically:

Use Automapper to convert one object to another with the same properties. Leave your business rule on your domain and let your repository just be your link to your database. In the rest try to evaluate what to do in each layer, and let each one do what is their responsibility.

6

The web project "sees" only the repository project and it is responsible for executing the business rules...

The ideal is to define the business rules in your project Domain and not in a repository.

Repository isolates domain objects (business related) from access code details and mapping of these objects with the database. That is, it adds a layer of separation between the layers of data access and domain access. He should not be responsible for executing business rules.

Your web project can "exercise" your domain project, your repository interfaces, without problems.

Now focusing on your doubt, at least for the example you put in the question I see no need for DTO unless you make many remote calls to recover lots of data. So I believe you can stop using DTO for some cases where you "don’t" are subject to latency problems inherent in remote communication/queries.

Using DTO is interesting in cases where we need to reduce the number of remote calls.

Exemplo: You need to make multiple remote calls, which increases the response time beyond acceptable levels. Here yes, interesting to use DTO, making a single query instead of several.

I’m using DTO’s because in most queries I don’t need all the data from the entities, so I don’t need to expose my entire entity to the view (and I also think it’s not a good practice).

Do not be tied to using DTO just for that reason, unless in some specific case as I mentioned.

You can create a method(s) in your Repository that returns the information that will be used in the View, fill in your Viewmodel object (like the User viewmodel of your example) and render this information in the View, without having to convert DTO to Viewmodel.

Exemplo: You can return a User in your Repository method and fill in your Viewmodel:

public class UsuarioRepositorio : IUsuarioRepository
{
   //Retorna Usuario invés de UsuarioDto
   public Usuario GetUsuario(int usuarioId){
      ...
   }
}  
  • Stop using DTO for some cases? If I don’t use DTO what could I use? Return my model to the view? In this case I think there should be a standard pq if sometimes use DTO and sometimes not the project can get confused. Another point. My user table has 15 fields. If I want to recover only 2 I use DTO. I see no reason to return all entities.

  • @Thiagocrizanto Stop using DTO for some cases? Yes. If I don’t use DTO what could I use? Your repository as exemplified. Return my model to the view? Yes. Using the pattern presented you will have the redundancy to do from/to. Then it is up to you to check what is best: to have a pattern and to be doing/to or using DTO only when really necessary. By filtering (p/ not returning all entities) in the repository methods you can filter the fields/entities you want, you don’t need DTO just for that. It may get confused if you don’t understand how to really use DTO.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.