7

I'm trying to learn MVC with ASP.Net and am reading Steve Sanderson's book. One thing I'm confused about is where to place the business logic?

For example, when deleting a product all Sanderson has is a method in his CartController that calls the Delete method on the productsRepository. This is strange to me because if there were any business logic, such as ensuring that the product isn't in anyone's shopping cart first, etc. it would have to either be in the products repository or the CartController.

Both of these seem like bad places to put business logic; the products repository is meant to be easily replaced with another (switching from using a db to using a session) and using the Controller means you are putting the business logic in the UI layer.

Shouldn't he be using a class that contains the business logic and calls the repository's delete method instead? The repository being a member variable of the business logic class'?

user660734
  • 255
  • 3
  • 9
  • Does this answer your question? [MVC: Where to put business logic?](https://stackoverflow.com/questions/18563229/mvc-where-to-put-business-logic) – Jonathan Hall Feb 13 '20 at 10:47

5 Answers5

8

I typically structure my MVC solutions in a way resembling the following:

  • X.Core
    • general extension methods, logging, and other non-web infrastructure code
  • X.Domain
    • domain entities and repositories
  • X.Domain.Services
    • domain services for orchestrating complex domain operations, such as adding a product to a shopping cart
  • X.Application.Core
    • application logic (initialization (route registration, IoC configuration, etc.), web-specific extensions, MVC filters, controller base classes, view engines, etc.)
  • X.Application.Models
    • view model classes
  • X.Application.Services
    • service classes that can return ViewModels by accessing repositories or domain services, as well as the other way around for updates
  • X.Application.Web
    • controllers, views and static resources

Some of these could be combined, but having them separate makes it easier to locate stuff and to ensure your layer boundaries are respected.

A typical controller action for showing the product cart might look like this:

public virtual ActionResult ProductCart()
{
    var applicationService = <obtain or create appropriate service instance>
    var userID = <obtain user ID or similar from session state>
    var viewModel = applicationService.GetProductCartModel( userID );
    return View( "Cart", viewModel );
}

A typical controller action for adding a product to the shopping cart might thus look something like this:

public virtual ActionResult AddProductToCart( int productID )
{
    var domainService = <obtain or create appropriate service instance>
    var userID = <obtain user ID or similar from session state>
    var response = domainService.AddProductToCart( userID, productID );
    return Json( new { Success = response.Success, Message = response.Message } );
}
Morten Mertner
  • 9,414
  • 4
  • 39
  • 56
  • Thanks, but I'm not sure that this answers my question. – user660734 Apr 02 '11 at 19:56
  • Updated with some code samples to illustrate the basic ideas of where to put your logic - hope this helps :) – Morten Mertner Apr 02 '11 at 20:23
  • Looking at your project structure I'm not sure I intuitively understand what goes where. This may be more to do with MVC, but it doesn't *feel* logical to me .. – flesh Apr 02 '11 at 20:50
  • I'd recommend reading up on DDD. Once you understand the terminology of aggregate roots vs entities, application vs domain services, etc. the project names become more illustrative than you'd initially think. See http://devlicio.us/blogs/casey/archive/2009/02/17/ddd-services.aspx and related posts in the series for a start. In MVC, I prefer using view models instead of exposing entities and use the application services to do this translation. – Morten Mertner Apr 02 '11 at 21:34
3

I also read Sanderson's first version of his book and it was fantastic - a very easy way to pick up and start using ASP.NET MVC. Unfortunately, you can't jump straight from the concepts in his book to writing a large maintainable application. One of the biggest hurdles is figuring out where to put your business logic and other concerns that lie between the UI and persistent storage (a concept called Separation of Concerns or SOC).

If you are interested, consider reading up on Domain Driven Design. I won't suggest that I know it perfectly, but serves as a good transition from Sanderson's sample applications into something that successfully separates UI concerns, business logic, and storage concerns.

My solution has a separate service layer. The controllers communicate with the service layer (using Dependency Injection - Ninject). The service layer has access to my domain objects / business logic and my repositories (NHibernate - also spun up with Ninject). I have very little logic in my views or controllers - the controllers serve a purpose of coordinator and I strive to keep my controller actions as thin as possible.

My domain layer (entities, business logic, etc.) has no dependencies. It does not have references to my Web project or to my Repository project. It is what is often referred to as POCO or Plain Old C#/CLR Objects.

EDIT: I noticed in one of your comments you are using EF. EF does not support POCO without using something called Code First (was in Community Technology Preview status when I checked last August). Just FYI.

Mayo
  • 10,544
  • 6
  • 45
  • 90
  • A good answer - could you maybe add an illustration of your project structure? – flesh Apr 02 '11 at 20:48
  • I have decided to do something like the following: 1) create a "service" class that has repository and entity member variables. The entity member variable is just the properties of the object i am modeling (POCO??). The controller will instantiate the service and set its repository and entity values. It then calls the service's Add() method for example. And I'm using Ninject too handle the dependencies too. Any thoughts on that before I redo my app to work like this? – user660734 Apr 02 '11 at 21:10
  • In my opinion you should have a productService class that contains references to the repositories for the products and for the shopping carts as private members. The instances of those repositories could be provided by dependency injection. This class should also contain a method that takes a product and the (target) shopping cart as parameters, queries whether this product is used in any other shopping cart and finally adds the product to the target shopping cart. – Frank Nov 06 '15 at 10:31
1

The reality is that there's no silver bullet to this answer, and it really is a contextual question at heart. I like to separate things as much as possible, and business logic is, if you think about it, just another layer in an app.

I worked up a BDD-inspired decisioning engine and released it as an OSS project available via NuGet. The project is called NDecision, and you can read about it on the NDecision project home page.

NDecision makes decision-tree business logic quite simple to implement, and if you're a fan of Gherkin syntax and Fluent coding practices you'll feel right at home using it. The code snapshot below is from the project site, and demonstrates how business logic could be implemented.

NDecision example code

This might not be a solution for you, but the idea is, if you already have your domain model in one assembly - a great idea and common practice as suggested in another answer earlier, there's no reason why you can't have another assembly with your "decision tree." NDecision was written with that in mind, to separate the logic into one independent layer.

Hopefully that'll be of some assistance.

brady gaster
  • 1,506
  • 1
  • 10
  • 15
0

If you want scalability, it's bad practice to use data access objects directly from the GUI. So in the example you mentioned, I think you should replace the repository that directly reads or writes to the DB by some business logic class that supports business operations.

Ilya Kogan
  • 21,995
  • 15
  • 85
  • 141
  • The repository pattern by definition is an abstraction. In my case, it is a wrapper for the entity framework. – user660734 Apr 02 '11 at 20:22
0

I'm trying to learn MVC with ASP.Net and am reading Steve Sanderson's book. One thing I'm confused about is where to place the business logic?

In the model (M in MVC), or domain layer. These are a separate set of types (preferably in a separate project) that represent domain models, services and repositories.

Max Toro
  • 28,282
  • 11
  • 76
  • 114