.net – Should controllers in an ASP.NET MVC web app call repositories, services, or both

asp.net-mvcnet

The controllers in my ASP.NET MVC web app are starting to get a bit bloated with business logic. The examples on the web all show simple controller actions that simply pull data out of a repository and pass it to the view. But what if you also need to support business logic on top of that?

Say, for instance, an action that fulfills an order also needs to send an e-mail out. Do I stick this in the controller and copy/paste this logic to any other actions that also fulfill orders? My first intuition would be to create a service like OrderFulfillerService that would take care of all this logic and have the controller action call that. However, for simple operations like retrieving a list of users or orders from the database, I would like to interact directly with the repository instead of having that call wrapped by a service.

Is this an acceptable design pattern? Controller actions call services when they need business logic and repositories when they just need data access?

Best Answer

Your controllers (in the MVC project) should be calling your objects in the Service project. The services project is where all the business logic is handled.

A good example is this:

public ActionResult Index()
{
    ProductServices productServices = new ProductServices();

    // top 10 products, for example.
    IList<Product> productList = productServices.GetProducts(10); 

    // Set this data into the custom viewdata.
    ViewData.Model = new ProductViewData
                         {
                             ProductList = productList;
                         };

    return View();
}  

or with Dependency Injection (my fav)

// Field with the reference to all product services (aka. business logic)
private readonly ProductServices _productServices;

// 'Greedy' constructor, which Dependency Injection auto finds and therefore
// will use.
public ProductController(ProductServices productServices)
{
    _productServices = productServices;
}

public ActionResult Index()
{
    // top 10 products, for example.
    // NOTE: The services instance was automagically created by the DI
    //       so i din't have to worry about it NOT being instansiated.
    IList<Product> productList = _productServices.GetProducts(10); 

    // Set this data into the custom viewdata.
    ViewData.Model = new ProductViewData
                         {
                             ProductList = productList;
                         };

    return View();
}

Now .. what's the Service project (or what is ProductServices)? that's a class library with your business logic. For example.

public class ProductServices : IProductServices
{
    private readonly ProductRepository _productRepository;
    public ProductServices(ProductRepository productRepository)
    {
        _productRepository = productRepository;
    }

    public IList<Product> GetProducts(int numberOfProducts)
    {
        // GetProducts() and OrderByMostRecent() are custom linq helpers...
        return _productRepository.GetProducts()
            .OrderByMostRecent()
            .Take(numberOfProducts)
            .ToList();
    }
}

but that might be all so hardcore and confusing... so a simple version of the ServiceProduct class could be (but i wouldn't really recommend) ...

public class ProductServices
{
    public IList<Product> GetProducts(int numberOfProducts)
    {
        using (DB db = new Linq2SqlDb() )
        {
            return (from p in db.Products
                    orderby p.DateCreated ascending
                    select p).Take(10).ToList();
        }
    }
}

So there you go. You can see that all the logic is in the Service projects, which means u can reuse that code in other places.

Where did i learn this?

From Rob Conery's MVC StoreFront media and tutorials. Best thing since sliced bread. His tutorials explain (what i did) in good detail with full solution code examples. He uses Dependency Injection which is SOO kewl now that i've seen how he uses it, in MVC.

HTH.