Search code examples
c#asp.net-mvc-3model-view-controllerviewmodelcontrollers

MVC 3 - Controllers and ViewModels - Which should contain most of the business logic?


Currently in my application and using the unit of work pattern and generic repository, all my controllers contain all of the business logic. I'm in the process of putting everything over to use ViewModels instead of the straight Model.

While this is a good idea, now comes a question that can significantly separate my business logic in the controllers. For controllers and ViewModels, which should contain most of the business logic?

I've tried a few ways to get my ViewModels to practically contain all the business logic. However, I do have to have an argument in my ViewModel's constructor that takes a Unit of Work. Is this a good idea?

My code smell tells me it is. However, I am just a little worried how this will be in consistency with controllers that perform actions that do not need ViewModels. Simply put, actions that do not require to pass a Model/ViewModel to a View; this case happens on actions that do redirects to other actions. Which means, my business logic can either stay in that action or I could separate that business logic into a function.

What is the best practice here?


Solution

  • For controllers and ViewModels, which should contain most of the business logic?

    None of those.

    I've tried a few ways to get my ViewModels to practically contain all the business logic. However, I do have to have an argument in my ViewModel's constructor that takes a Unit of Work. Is this a good idea?

    imho It's a very bad idea. First of all you are breaking several of the SOLID principles. Bundling all code into the view model makes it hard to test. What if you want to use some of the business logic in another view? Do you duplicate that code?

    What is the best practice here?

    Let's go back to the MVC pattern first. It's a quite wide definition but knowing it should give you a feeling of what you should place where.

    • The "Model" in MVC is really everything that is used to pull the data together. It can be webservices, a business layer, repositories etc.

    • The view is all code that generates the HTML (since we are talking about web).

    • The controller should be considered to be a glue between the model and the view. Hence it should take the information from the Model and transform it into something usable by the view.

    The problem with that structure is that it's quite easy to "leak" layer specific information into the other parts of the pattern. Hence Microsoft introduced ViewModels into their implementation of MVC.

    In this way we can remove all rendering logic from the views and put it into the ViewModel. Instead of doing this in your view:

    <span>@(model.Age == 0 ? "n/a" : model.Age)</span>
    

    you put that code inside the ViewModel instead and simply call @model.Age. In this way you don't have to duplicate that code in all views that are using your view model.

    The answer to your question about the ViewModel is that it should only contain logic which is used to render the information from the "Model" properly.

    As for the controller, I would not put any business logic into it either. First of all, it makes it very hard to test your logic. Then you add more responsibilities to it (and by doing so breaking SRP). The only logic that is valid in the controller is to take the information from the ViewModel and transform it into something usable by the "Model" and vice versa.

    Hope that answers your question.

    Update

    I would create a separate project and add classes to it. Then just add a reference from your webproject and call those classes in the controllers.

    I would also start using an inversion of control container to get those dependencies created for me automagically.

    Autofac can both discover your services for you (zero-configuration) and inject itself into MVC.

    To follow the separated interface pattern create the following projects:

    • YourProject.BusinessLayer <-- Add your classes here
    • YourProject.BusinessLayer.Specification <-- Add you interfaces that defines your business layer here.
    • YourProject.Mvc <-- The MVC project.

    The "Specification" project can be used to make it easier to test things and to make it easier to switch implementation (might be only a few classes and not necessarily the entire business layer). Read up on "Seperated Interface Pattern"