ASP.NET MVC – Role of ViewModels in MVC and 3-Tier Architecture

asp.net-mvccdesign-patterns

I'm designing a 3-tiered application using ASP.NET MVC 4. I used the following resources as a reference.

I have the following desingn so far.

Presentation Layer (PL) (main MVC project, where M of MVC was moved to Data Access Layer):

MyProjectName.Main
    Views/
    Controllers/
    ...

Business Logic Layer (BLL):

MyProjectName.BLL
    ViewModels/
    ProjectServices/
    ...

Data Access Layer (DAL):

MyProjectName.DAL
    Models/
    Repositories.EF/
    Repositories.Dapper/
    ...

Now, PL references BLL and BLL references DAL. This way lower layer does not depend on the one above it.

In this design PL invokes a service of the BLL. PL can pass a View Model to BLL and BLL can pass a View Model back to PL.

Also, BLL invokes DAL layer and DAL layer can return a Model back to BLL. BLL can in turn build a View Model and return it to PL.

Up to now this pattern was working for me. However, I've ran into a problem where some of my ViewModels require joins on several entities. In the plain MVC approach, in the controller I used a LINQ query to do joins and then select new MyViewModel(){ ... }. But now, in the DAL I do not have access to where ViewModels are defined (in the BLL).

This means I cannot do joins in DAL and return it to BLL. It seems I have to do separate queries in DAL (instead of joins in one query) and BLL would then use the result of these to build a ViewModel. This is very inconvenient, but I don't think I should be exposing DAL to ViewModels.

Any ideas how I can solve this dilemma? Thanks.

Best Answer

main MVC project, where M of MVC was moved to Data Access Layer

Common misconception. The M of MVC has nothing to do with data, despite the many examples and tutorials that claim so.

M is your ViewModel and should reside in your MVC project. The ViewModels you have in your BLL are actually to be named DataContracts or BusinessModels.

In your controller you have something comparable to this:

Get(id):
    dataContract = _service.Get(id);
    viewModel = Map(dataContract);
    return viewModel

In your service, something like this:

Get(id):
    dataModel = _dataAccess.Get(id);
    dataContract = Map(dataModel);
    return dataContract;

And in the DataAccess, you perform the proper joins according to the object requested. You are however of course free to add custom methods to your DataAccess when required, so your service can call those methods:

GetWithBars():
    dataModels = _repository.Query("select from foos join bars");
    return dataModels;
Related Topic