11

When planning the architecture for a mid-large scale MVC web application how do you implement the layers to be as decoupled as possible and easy to test? (basically follow best practices) Let's say I'm using code first as my data access.

I struggle with what to define "business logic" as, and how it is meant to interact with the data layer. Taking a vehicle sales application as an example, would business logic be classes that performed tasks such as calculating the tax band for given vehicles, comparing mile per gallon statistics etc? As for the business entities (e.g. Cars, Vans, Motorcycles) I would put these in the data layer along with my DataContext class.

Also what would constitute application logic as opposed to business - I'm guessing things like session / user input validations?

So for example, a car controller might return an action/view result that lists the top ten cars filtered by type and best mpg. So let's say I have an ICarRepository 'carRepo' injected into my controller(using the repository pattern / DI), I filter my cars from an action method parameter e.g. var cars = carRepo.getCarsByType("hatchback");

So I've kept the data access knowledge out of my controller using a repository, now to keep the business logic out of the controller using a domain model - var result = new MpgCalculator(cars); - Let's say I need the calculator class because it needs to perform additional logic to calculate the best fuel efficiency, more than just loading / filtering entities from the DB. So now I have a data set for my view to render that used a repository to retrieve from the data access layer, and domain specific object to process and perform business related tasks on that data.

Am I making mistakes here? do we still need to use the repository pattern or can I just code against an interface to decouple the ORM and test? On this topic, as my concrete data access classe(s) dbcontext are in the data layer, should the interface definitions go into the domain/business layer meaning that if the data access technology is ever changed, my other layers aren't effected?

From what I have studied thus far my structure looks like this:

MVC Internet Application -> The standard internet project - models in here are ViewModels

Domain/Business layer -> business specific classes/models that controllers can use to process domain entities from the data layer before passing on to the relevant views

Repository abstraction necessary? -> I hear lots of debate on this, especially when using an ORM

Data layer -> Entity classes (Car, Van, Motorcycle), DbContext - Concrete data access technology layer

2 Answers 2

26

You've got a lot of moving parts in your question, touching on a lot of concepts, but here's my basic advice when it comes to how to think about a mid-to-large scale MVC application:

Presentation <---> Business Logic <---> Data Access

Firstly, it's best to not think of the the app as "an MVC application". It's an application that uses the MVC pattern as its presentation component. Thinking about it this way will help you separate out your business logic concerns from your presentation concerns. Perhaps it's ok for small applications to pile everything down to database access into the MVC structure, but it'll quickly become untenable for a mid-to-large application.

MVC (Presentation)

In your app, the ASP.NET MVC component should deal with transforming business data for display purposes (Models), displaying the user interface (Views), and communication issues such as routing, authentication, authorization, request validation, response handling, and the like (Controllers). If you have code that does something else, then it doesn't belong in the MVC component.

Repository/ORM (Data Access)

Also in your app, the data access layer should be concerned with retrieving and storing persistent data. Commonly that's in the form of a relational database, but there are many other ways data can be persisted. If you have code that isn't reading or storing persistent data, then it doesn't belong in the data layer. I shared my thoughts on the ORM/Repository discussion previously on SO, but to recap, I don't consider an ORM to be the same thing as a Repository, for several reasons.

Business Logic

So now you have your presentation layer (MVC), and your data layer (repository or ORM) ... Everything else is your business logic layer (BLL). All of your code that decides which data to retrieve, or performs complicated calculations, or makes business decisions, should be here. I usually organize my business logic in the form of 'services', which my presentation layer can call upon to do the work requested. All of my domain models exist here.

Your Approach

This is where your approach breaks down a little for me. You describe your MVC controller as the place you would get data from the repository, and call upon the MPGCalculator to do some work, etc. I would not have my controller do any of this, but instead would delegate all of this off to a service in the BLL.

In other words, I would not inject a repository and MPGCalculator into the controller, that's giving the controller too much responsibility (it's already handling all of the controller stuff I mentioned above). Instead, I would have a service in the BLL handle all of that, and pass the results back to the controller. The controller can then transform the results to the correct model, and pass that on to the correct view. The controller doesn't have any business logic in it, and the only things injected into the controller would be the appropriate BLL services.

Doing it this way means your business logic (for example, given a set of vehicles, calculate the MPG and sort best to worst) is independent from presentation and persistence concerns. It'll usually be in a library that doesn't know or care about the data persistence strategy nor the presentation strategy.

7
  • Hi Eric, excellent reply - regarding the repositories, I assume the concrete classes would live in the data access layer and the 'ICarRepository' etc in the business/service layer? Then I could inject services in to my controller which may contain 1 or more repositories depending on the requirements? Commented Oct 5, 2013 at 22:27
  • @MichaelHarper Yes, that sounds like a perfectly good way of going about it.
    – Eric King
    Commented Oct 5, 2013 at 22:42
  • 1
    While authentication is a controller concern (different UIs authenticate differently) I'd say authorisation is business logic and belongs in the business layer. Do you agree?
    – tom
    Commented Nov 13, 2014 at 9:22
  • 1
    @tom Yes, you have a good point. I was thinking simple authorization as in does user have access to this route, but there can be a lot more to it than that. The "a lot more to it" part belongs in the business layer.
    – Eric King
    Commented Nov 13, 2014 at 15:09
  • 1
    @HunterNelson If you're mapping into a viewmodel, then the mapping should occur where the view is, in the presentation layer. It wouldn't make sense anywhere else.
    – Eric King
    Commented Oct 15, 2018 at 17:55
0

It looks like everything is correct for your structure. The only thing that I am not sure about is that you mention that the models in MVC are "ViewModels" and that your controllers talk to the domain layer. I think this makes sense if your default pattern is to use the controller to access the domain layer and then using your "ViewModels" as more view-specific compilations of information from multiple domain entities as makes sense for that particular view. If that is what you are doing, then you are likely ok.

There is a school of thought that you should have a complete abstraction of your domain layer in your MVC application if you are going to have any. Personally, the thought of doing that in an enterprise application causes me severe mental pains.

I prefer using the repository pattern to manage the access to the Data Layer as it enhances testability and flexibility. The two things that tend to make the most drastic changes are the UI and the database. Imagine if some of the information that you are pulling directly out of the database is changed so that it has to be retrieved from a service call rather than a database call, or some of the information is moved to a different database requiring a different .edmx file. The repository pattern provides abstraction to support this.

1
  • Thanks for the reply William 😊 I'd consider my business objects/logic and domain entities as 'models' which the controller uses to process user actions and viewmodels as view specific models which could contain groups of models etc. Commented Oct 4, 2013 at 21:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.