Service Layer, MVC Controllers, and Dependency Injection

This is part 2 of a 3 part series –

1)  Unit of Work, Repository, Entity Framework, and Persistence Ignorance

2) Service Layer, MVC Controllers, and Dependency Injection

3) Unit Testing using Moq

In part 1, I discussed how we can enable persistence ignorance in our MVC architecture through the implementation of the Unit of Work and Repository design patterns. And in so doing we can improve the maintainability and extensibility of our architecture by loosely coupling the controllers and the persistence layer. The controllers are not dependent on a specific data-access technology. We can switch technologies without needing to change any controller coding or references. And we can more easily isolate the controllers when performing unit testing.

With MVC, when a request is received it is routed to a controller action method. The action method interprets the user input, determines what logic to perform, and what action result to return. Having all application logic contained in the controller actions works well for small applications. However, when an application grows in complexity we may find that our action methods become quite complex and lengthy. Also, we may find that application logic gets duplicated if it is used in more than one controller.

For example, if we are building a store application then we may have to accommodate the following logic when the customer has completed the checkout process and confirms the placement of an order –

  1. Validate the order details
  2. Change the status of the order to confirmed
  3. Generate an order confirmation reference
  4. Send an order confirmation email to the customer, including the reference
  5. Empty the customer’s basket
  6. Assign the order to the order processing pipeline
  7. Return a confirmation view as a response to the request, including the reference

This translates into very many lines of code, and so the action method will be quite lengthy. With even moderately complex applications, our controllers soon become bloated and difficult to maintain.

Also, step 5 above involves emptying the customer’s basket. In addition, to the checkout process, we may have a BasketController with an “Empty” action which may respond to the user clicking on an Empty Basket link. This specific piece of logic is therefore required in more than one controller.

We will therefore benefit from refactoring the code to make it more modular and easier to understand. 

We can create one or more helper classes in our MVC project, and create helper methods that can be utilized by the controller actions.

A more extensible way of refactoring the code is to attempt to separate the logic into presentation logic and business logic layer. Keep the presentation logic in the controller actions, and accompanying helper classes where required. But move the business logic into a separate class library. By separating the business logic from the UI, we are able to implement alternative UIs – a Web API for example – that utilize the same logic. We are adhering to the single responsibility principle, and as such we are making our code easier to understand, maintain and extend.

What I am going to outline in this post is how to implement a business logic layer. I will refer to the layer from now on as a service layer. I think this is a good pragmatic description, although there is a lot of debate on whether this should be called a service layer, or business logic layer, or even application domain layer. There is further debate on the definition of application domain, which leads into debate on whether the business logic should be encapsulated with the model. Bottom line is that there is more than one way to skin a cat. I don’t really care about the semantics. What I care about is having a set of workable patterns that are easy to understand, implement, extend, and maintain.

To quote Fowler, a Service Layer –

Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.

Enterprise applications typically require different kinds of interfaces to the data they store and the logic they implement: data loaders, user interfaces, integration gateways, and others. Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic. The interactions may be complex, involving transactions across multiple resources and the coordination of several responses to an action. Encoding the logic of the interactions separately in each interface causes a lot of duplication.

The implementation of the Service Layer pattern that I use is pretty much identical to the implementation outlined in the following guide by Stephen Walther –

http://www.asp.net/mvc/overview/older-versions-1/models-%28data%29/validating-with-a-service-layer-cs

In particular I have adopted the use of the ModelStateWrapper class to pass business logic errors from the service layer to the client.  There are some small differences, which I will outline below.

IService

The first thing is to create a contract for the service layer classes. IService is this contract, and contains a single method Initialize, which accepts an instance of a class that implements IValidationDictionary. It is this class that will be used to pass business logic validation errors back from the service layer to the client – MVC controller in my case.

namespace Framework.Service
{
    public interface IService
    {
        void Initialize(IValidationDictionary validationDictionary);
    }
}

IValidationDictionary

namespace Framework.Service
{
    public interface IValidationDictionary
    {
        void AddError(string key, string errorMessage);
        bool IsValid { get; }
    }
}

ValidationDictionary

using Framework.Service;
using System.Web.Mvc;

namespace Framework.Mvc
{
    public class ValidationDictionary : IValidationDictionary
    {
        private ModelStateDictionary _modelState;

        public ValidationDictionary(ModelStateDictionary modelState)
        {
            _modelState = modelState;
        }
        public void AddError(string key, string errorMessage)
        {
            _modelState.AddModelError(key, errorMessage);
        }

        public bool IsValid
        {
            get { return _modelState.IsValid; }
        }
    }
}

Validation dictionary is an MVC specific implementation of IValidationDictionary. It has a constructor that accepts an instance of System.Web.Mvc.ModelStateDictionary. The service layer can call the AddModelError method to add business logic validation errors to this dictionary. MVC can then utilize this dictionary to pass these errors back to the view – as you will see later.

An alternative method for communicating business validation errors back to controller, is to derive a ValidationException class from System.Exception, and have the service raise this exception when a validation error occurs. The controller action will Catch this exception and then deal with it appropriately. This is widely adopted, but I’ve always followed the practice of only utilizing exceptions for ‘exceptional’ not-expected occurrences. I’ve seen too many applications in my time where developers have attempted to use exceptions for implementing logic, and then ended up obnubilating the sources of real errors.

IOrderService

using BikeStore.Model;
using Framework.Service;
using System;
using System.Collections.Generic;

namespace BikeStore.Service
{
    public interface IOrderService : IService
    {
        string CreateOrder(int customerId, int basketId, int customerAddressId, int paymentCardId);
        List<Order> GetOrdersByCustomer(int customerId);
    }
}

I create an interface for each service, in part to help me mock the services when unit testing controllers.

IOrderService contains IService. This is to ensure that any concrete implementation of IProductService implement the Initialize method. If I was sure that all implementations of IProductService were to utilize unmanaged resources then I could also have IOrderService contain IDisposible. However, this may not always be the case, so I have not done this. Instead I have implemented IDisposible directly in the concrete OrderService class.

The IOrderService code shown above contains 2 methods, CreateOrder and GetOrdersByCustomer. In practice it will contain many more methods, including CRUD methods that do not include any business logic. These methods will simply pass through the calls to the respective repository. There may be service instances that only contain these pass-through calls, and do not include any business logic at all. Arguably there is some redundancy in this case. But I prefer to have this redundancy rather than have the client code directly referencing the repositories and unit of work. By having the controllers always dealing with service classes, and never with the repositories or unit of work, we are being consistent throughout the application. Also we are adding extensibility points into our design, hence we are future proofing the application.

OrderService

using BikeStore.Data;
using BikeStore.Model;
using Framework.Service;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BikeStore.Service
{
    public class OrderService : IOrderService
    {
        private IBikeStoreUnitOfWork _unitOfWork;
        private IValidationDictionary _validatonDictionary;

        #region Constructors and Initializers
        public OrderService(IBikeStoreUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        public void Initialize(IValidationDictionary validationDictionary)
        {
            _validatonDictionary = validationDictionary;
        }
        #endregion

        #region Order
        public string CreateOrder(int customerId, int basketId, int customerAddressId, int paymentCardId)
        {
            // Validate payment details
            PaymentCard paymentCard = _unitOfWork.PaymentCardRepository.Get(pc => pc.PaymentCardId == paymentCardId).First();

            if (paymentCard.ExpiryDate > DateTime.Now)
            {
                _validatonDictionary.AddError("PaymentCard", "The payment card has expired.");
                return null;
            }
            else
            {
                // Process the order
                .......
                return order.OrderReference; 

            }
        }

        public IEnumerable<Order> GetOrdersByCustomer(int customerId)
        {
            return _unitOfWork.OrderRepository.Get(o => o.CustomerId == customerId);
        }
        #endregion

        #region Dispose
        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _unitOfWork.Dispose();
                }
            }
            this.disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}

OrderService implements IOrderService, and also IDisposible as it utilizes BikeStoreUnitOfWork, which is disposible.

BikeStoreUnitOfWork is injected into the constructor – using Unity in my case – as discussed below.

I have implemented only part of the CreateOrder method, but enough to show how business validation errors can be added to the ValidationDictionary instance. In this case I am checking to see if the payment card has expired. If yes, then an appropriate error is added to the dictionary, and the method returns null.

CheckOutController

using AutoMapper;
using BikeStore.Helpers;
using BikeStore.Service;
using Framework.Mvc;
using System.Web.Mvc;

namespace BikeStore.Controllers
{
    [Authorize]
    public class CheckOutController : Controller
    {
        private IOrderService _orderService;

        public CheckOutController(IOrderService orderService)
        {
            _orderService = orderService;
            _orderService.Initialize(new ValidationDictionary(this.ModelState));
        }

        // POST: CheckOut/Payment
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Payment([Bind(Include = "customerAddressId,paymentCardId")] int customerAddressId, int paymentCardId)
        {
            if (ModelState.IsValid)
            {
                // Create Order (includes sending confirmation email)
                string orderRef = _orderService.CreateOrder(GetCustomer().CustomerId, (int)BasketHelper.GetBasketId(this.ControllerContext), customerAddressId, paymentCardId);

                if (orderRef == null)
                    return View();
                else
                    return View("Confirm");
            }

            return View();
        }

        // Dispose
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _orderService.Dispose();
                _mapper.Dispose();

            }
            base.Dispose(disposing);
        }
    }
}

CheckOutController has an instance of OrderService injected into it’s constructor. The constructor calls the OrderService.Initialize method, and passes in a ValidationDictonary which in turn contains the controller’s ModelState object. Validation errors are therefore added directly into the ModelState object.

The Payment action method call the OrderService.CreateOrder method. If there is a validation error, then the CreateOrder method returns null, the action method responds with the Payment view, passing the ModelState to the view.

Depedency Injection

I use Unity to inject instances of IBikeStoreUnitOfWork into the service classes constructors, and to inject instances of the service classes into the controllers.

I outline the steps required to set up Unity in the following blog post – https://datatellblog.wordpress.com/2015/02/05/asp-net-5-vnext-dependency-injection/

 

 

 

 

 

 

 

 

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s