2

We do simple data entry validation with attributes on the viewmodel. Think format of an email address, mandatory clientname, format of a date, the housenumber being numeric etc.

Now there are also more complex validations which will hit the database and external services: check if the id is a valid one (external service), see if the person (probably) already is present in the database based on name/address (database action), check the address/zip code combination (external service).

In my opinion this logic should not be in the controller but in a separate validation component / validation service (which gets DI'd in the controller).

I would like your opinion on this.

  • If you can give us a bit more info.. Where does the insert into the database happen? Do you use a service layer? A repository? – Alternatex Aug 24 '16 at 7:38
  • unfortunately most of the business logic at this point is in the controller, and there is one (large) repository class. The reason I ask the question is that I want to give more structure to the code, and as a lot is written about layering a solution, the placement of this validation logic I don't read about a lot. My proposed setup would be (besides these validation services) to have an application service for each controller, domain services en models per logical entity (or aggregate root) and also a repository per logical entity / aggregate root. – Michel Aug 24 '16 at 7:44
4

Disclaimer: The following is my approach and not holy scripture. I'm at work so I wrote this swiftly and some code may not work/compile!

Controllers

They are components of the presentation layer so they shouldn't be concerned with business or database logic. Validation logic is a subset of business logic.

Services

I implement the Service Layer pattern which is basically a bunch of classes that are loosely associated with your domain models and act upon them. These service classes do all of the business logic. This includes the database stuff. If you're not too keen on putting database logic here you can implement the Repository pattern and inject the repository classes into your service classes.

So now the flow of things is becoming clear

Controller -> Service -> Repository

and this equates to

ViewModel to Model mapping -> Validation and other business logic -> CRUD operations

Every service class is described by an interface so now you can use DI to inject them into your Controllers like so:

readonly IClientService _clientService;

public ClientsController(IClientService clientService)
{
    _clientService = clientService;
}

It's now clear exactly what dependencies your Controller has since it's all there in the constructor. The Controllers are also cleaner since all of the business logic is in the service layer. Here's an insert example:

[HttpPost]
public ActionResult Insert(ClientViewModel viewModel)
{
    if (!ModelState.IsValid)
        return View(viewModel);

    var client = new Client();
    viewModel.MapTo(client);
    _clientService.Insert(client);

    return View(viewModel);
}

Now the validation part

The type of validation that constitutes checking the database is what I call deep validation. I put simple (shallow) validation in the ViewModel (like yourself) and deep validation in the business layer aka services.

So the deep validation that needs to take place when you insert a Client is found in the Insert method of the ClientService. All the database checks are done there, maybe by calling a repository or just directly in the service if you're not using repositories.

So if the client is invalid how do we return the validation errors back to the Controller?

We throw an exception since that's what exceptions are for. But it's going to be our own custom exception:

public class MyException : Exception
{
    public MyException()
    {
    }

    public MyException(string message)
        : base(message)
    {
    }

    public MyException(string message, Exception inner)
        : base(message, inner)
    {
    }
}

So when things are invalid (in our ClientService):

public void Insert(Client client)
{
    if (client == null)
    {
        throw new MyException("This thing's not right..");
    }

We've made sure the insert failed at the soonest possible moment, which is good.

Now all that's left is to catch these custom exceptions in our Controller. You can do this many ways. One is to use try/catch clauses:

try
{
    _clientService.Insert(client);
}
catch (MyException e)
{
    // Add error to ModelState perhaps
}

but they will thoroughly litter your Controller.

The proper MVC approach is to catch all the errors in the OnException method of your Controller:

protected override void OnException(ExceptionContext exceptionContext)
{
    exceptionContext.ExceptionHandled = true;

    if (exceptionContext.Exception is MyException)
    {
        // Perhaps add to ModelState
    }
    else
    {
        // Add a generic error message to ModelState and log error
    }

    filterContext.Result = new ViewResult
    {
        ViewName = ...
    };
}

You can put this code in a BaseController from which all of your other Controllers will inherit in order to avoid code duplication.


That's the basic framework I work in. I hope I didn't make things too complicated or confusing.

  • So you put in in the logic where the object is saved, in the business service, not in a separate 'validation' object/service? – Michel Aug 24 '16 at 12:17
  • Nice way btw to work with the outcome of the validation: throw errors and let the onexception method add the errors to the modelstate. I wonder if this also could be done in a filter, some kind of exceptionfilter, which handles all the validationexceptions and let other exceptions go unhandled for other errorfilters? – Michel Aug 24 '16 at 12:19
  • @Michel Exactly. Having a separate (or global) validation service may seem like a good idea in the beginning but it tends to get complicated as an application grows. You might have different validation rules for inserting than for updating so with this approach everything is in its logical place and easy to find. It also makes it impossible for a developer to skip/forget the validation process before sending a model to the service for inserting/updating. On your 2nd comment, I haven't tried other exception filtering approaches so I can't recommend anything different currently. – Alternatex Aug 24 '16 at 12:25
  • This is also pretty much how I do it, as I previously answered here and here. – Eric King Aug 24 '16 at 14:53
-1

You can create folder named "Repositories" under your project and add class named for example "ValidationRepository" and inject to controller.

  • 1
    Thanks, I was however looking for an answer of the concept of putting the more complex validation in a separate validation-component/class, not so much for a way of implementing it. – Michel Aug 24 '16 at 7:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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