views:

75

answers:

1

I have the following domain class:

public class Product
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Product> RelatedProducts { get; set; }
}

I have the following DTO class:

public class ProductDTO
{
    public ProductDTO(Product product)
    {
        Id = product.Id;
        Name = product.Name;
    }

    public Guid Id { get; private set; }
    public string Name { get; private set; }
}

I have the following method in my service:

public ProductDTO GetBySlug(string slug)
{
    Product product = productRepository.GetBySlug(slug);
    return (product != null) ? new ProductDTO(product) : null;
}

I have the following action in my controller:

public ActionResult Details(string slug)
{
    ProductDTO viewModel = productService.GetBySlug(slug);
    return View("Details", viewModel);
}

After reading around a bit is my understanding that using a DTO as a viewmodel is ok as the current scenario is simple and straight forward. My confusion occurs when the data that I want to return gets a bit more complex. Let's assume that I also want to return the list of related products to the view as well. Where would I add this list?

I read that a DTO is a flattened version of your domain entity that is used to transfer data. Does that mean that a generic list containing the related products should not be allowed inside the DTO? The answer I received so far suggests so. How do I then get the related products to the controller?

One option is:

Instead of returning the ProductDTO in the service I would create a new class that holds ProductDTO and the List of type ProductDTO for the related products and return it from the service. In the controller I would then either pass the new class to the view or create a separate ProductViewModel that holds ProductDTO and the List of type ProductDTO for the related products, populate it and pass that to the view.

Is this a good or a bad idea? Why?

Thanks

+1  A: 

I wouldn't place the list inside the DTO at all, because it does not naturally belong there. And I am also not sure what you mean with 'wrapper class'. All you need is a list of products, and it is perfectly OK to have another method on the service which returns this list.

Consequently, you would have something like this in your service:

public IList<ProductDTO> GetRelatedProducts(ProductDTO productDTO)
{
    ...

The most important idea behind a viewmodel (the thing that is called service above) is that it mediates between the UI and the business model. In other words: It orchestrates and aggregates the business model in a way that is relevant to the UI. And if the UI wants a list of related products at some point, then the service has to deliver it. It's really as simple as this, and it is completely irrelevant here whether the business model itself also has this concept.

HTH! Thomas

P.S. If your DTOs get larger and your lists longer, you might consider to introduce another (simplified) DTO with only the name and some sort of identifier to reduce the amount of unnecessary data you have to retrieve from the repository.

Thomas Weller
Thomas, thanks for your answer. I edited the questions and added that the related products are already fetched from the repository in the above code. The domain model product entity has a list with the related products already loaded. I am just confused as to how I get the related products to the view. It's helpful to know that the list does not belong in the DTO, that makes things a bit easier.
Thomas
Thomas, I edited the question a bit to try to make things a bit clearer. If you have a second, can you take another look?
Thomas
If your Business Model - Product already has a 'GetRelatedProducts()' method, your ProductDTO might have this as well (returning a read-only list of ProductDTOs). If you need to deliver the related products to your UI without having originating ProductDTO, it may be easier to have an extra service method...Both is equally possible and 'valid', it really all depends on what makes more sense in your UI context/workflow.
Thomas Weller
Yes, currently product and related products are populated at the same time using one database transaction. So in this case it is valid and makes sense to add a readonly list of ProductDTO to my ProductDTO?
Thomas
Sounds like that'd be the easiest and simplest thing in this case. So you should go for it...
Thomas Weller