views:

97

answers:

2

I have the following domain entity:

public class CartItem
{
    public virtual Guid Id { get; set; }
    public virtual Guid SessionId  { get; set; }
    public virtual int Quantity  { get; set; }
    public virtual Product Product  { get; set; }
}

I have the following DTO:

public class CartItemDTO
{
    public CartItemDTO(CartItem cartItem)
    {
        Id = cartItem.Id;
        Quantity = cartItem.Quantity;
        Name = cartItem.Product.Name;
        Price = cartItem.Product.Price;
    }

    public Guid Id { get; private set; }
    public int Quantity { get; private set; }
    public string Name { get; private set; }
    public decimal Price { get; private set; }
}

The current work flow is pretty simple: my repository returns a IEnumerable of type CartItem. My service transforms it to dto's (CartItemDTO). My controller then passes it to the view. So far so good.

Now I want to implement a total for each of the line items. I added the following property to CartItemDTO.

public decimal Total { get; private set; }

I then added the following code to the CartItemDTO constructor.

Total = cartItem.Quantity * cartItem.Product.Price;

My first question is if this is a best practices approach? If not, why? Should I have added the Total property somewhere else? If so, why?

I also wanted to implement a total for the entire cart so I create a new class (below) and modified my service to return it.

public class CartItemResult
{
    public CartItemResult(IEnumerable<CartItemDTO> result)
    {
        CartItems = new List<CartItemDTO>(result);
        Total = result.Sum(total => total.Total);
    }

    public IList<CartItemDTO> CartItems { get; private set; }
    public decimal Total { get; private set; }
}

I could now either pass the new class to the view or create a separate ViewModel and pass the content of the new class to the ViewModel and pass that to the view.

My second question is, again if this approach is a best practices approach? If not, why and what should I have done differently?

+1  A: 

Personally, for data retrieval, I would suggest returning the POCO entity from the repository and mapping it to the ViewModel in the controller (Automapper). There's no need to have an intermediary object really.

It is up to your design where the Total property should be implemented. Is that property used in the domain for any business logic? Could it be? If not then you might consider it merely a presentation concern and suitable for the ViewModel.

For data update - return something like a CartItemUpdateViewModel in your UI which maps to a domain entity of CartItemUpdateDetails in the controller which is then passed to the repository method.

As far as your question regarding the CartItemResult goes. I would personally be putting the Total logic in a ViewModel and populate that ViewModel in the contrller from a repository method returning IEnumerable<CartItem>.

Take a look at the WhoCanHelpMe project for a good enterprise application showcasing DDD.

David Neale
A: 

Hello,

DTO is pure class for transfering data. It should not contain any logic. You have added domain driven design tag so I think you want to use domain objects and domain services. It is responsibility of your domain object (CartItem) to compute total for item. Domain object is data + business logic and rules working with that data. Computing total is business logic. Also for your second part (CartItemResult) this is usually handled by something called domain service. Domain service provides business logic which is not related to single domain object but instead works with many domain objects.

But you should also think about complexity of your project. Do you really need this? Don't use patterns just because they exists, always find reason to use them. Approach mentioned by David Neale is also correct for some applications.

Best regards, Ladislav

Ladislav Mrnka