Asp.net-mvc – Where does the “business logic layer” fit in to an MVC application

asp.net-mvcbusiness-logic-layerdesign-patternsmodel-view-controller

First, before anyone screams dupe, I had a hard time summarizing it in a simple title. Another title might have been "What is the difference between a domain model and MVC model?" or "What is a model?"

Conceptually, I understand a Model to be the data used by the views and controller. Beyond that, there seems to be a great deal of differing opinions on what makes up the model. What's a domain model, versus an app model, vs a view model, vs a service model, etc..

For example, in a recent question I asked about the repository pattern, I was told point blank that the repository is part of the model. However, I have read other opinions that the model should be seperated from the persistence model and the business logic layer. After all, isn't the Repository pattern supposed to decouple the concrete persistence method from the model? Other people say there is a difference between the Domain model and the MVC model.

Let's take a simple example. The AccountController that is included with the MVC default project. I've read several opinions that the Account code included is of poor design, violates SRP, etc.. etc.. If one were to design a "proper" Membership model for an MVC application, what would that be?

How would you seperate the ASP.NET services (Membership provider, role provider, etc..) from the model? Or would you at all?

The way I see it, the model should be "pure", perhaps with validation logic.. but should be seperate from business rules (other than validation). For example, let's say you have a business rule that says someone must be emailed when a new account is created. That doesn't really belong in the model in my view. So where does it belong?

Anyone care to shed any light on this issue?

Best Answer

The way I have done it - and I'm not saying it is right or wrong, is to have my View and then a model that applies to my view. This model only has what is relevant to my view - including data annotations and validation rules. The controller only houses logic for building the model. I have a service layer which houses all business logic. My controllers call my service layer. Beyond that is my repository layer.

My domain objects are housed separately (in their own project, actually). They have their own data annotations and validation rules. My repository validates the objects in my domain before saving them into the database. Because every object in my domain inherits from a base class which has validation built in, my repository is generic and validates everything (and requires it inherits from the base class).

You might think that having two sets of models is duplication of code, and it is to an extent. But, there are perfectly reasonable instances where the domain object is not appropriate for the view.

Case in point is when working with credit cards - I have to require a cvv when processing a payment, but I cannot store the cvv (it is a $50,000 fine to do so). But, I also want you to be able to edit your credit card - change of address, name, or expiration date. But you aren't going to give me the number or the cvv when editing it, and I certainly am not going to put your credit card number in plain text on the page. My domain has these values required for saving a new credit card because you give them to me, but my edit model doesn't even include the card number or cvv.

Another benefit to so many layers is that if architected correctly, you can use structuremap or another IoC container and swap out pieces without detrimentally affecting your application.

In my opinion, controller code should only be code targeted at the view. Show this, hide that, etc. The service layer should house the business logic for your app. I like having all of it in one place so it's easy to change or tweak a business rule. The repository layer should be relatively dumb - devoid of business logic and only query your data and return your domain objects. By separating the view models from the domain model, you have much more flexibility when it comes to custom validation rules. It also means you don't have to dump every piece of data into your view in hidden fields and push it back and forth between the client and server (or rebuild it on the backend). Your view model will then house only the information relevant to the view - and it can be customized to have bools for view logic or counts or enums so that the view itself isn't cluttered up with complicated logic statements like

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

While everything seems spread out and over-layered, it has a purpose for being architected this way. Is it perfect? not really. But I do prefer it to some past designs of calling repositories from the controller and having business logic mixed in the controller, repository, and model.