views:

177

answers:

6

Say that I've got a web application that can store Persons in a database. Every Person must have a unique email address (or username or whatever). If a user attempts to add a Person with an email address that already exists, the form should be returned with an error message (like it would during a typical validation failure).

How is this kind error most typically bubbled up from the service layer to the controller and then to the view? Should the service method throw an exception for the controller to catch, or return a value or some kind of result object?

If I eventually want to use my service layer to generate a web service, does this change how I might proceed?

Any suggestions or links to best practices / sample applications would be appreciated.

+1  A: 

One way to handle this is to create a response object for your addPerson operation that has a status code. For example:

class AddPersonResponse {
    private Person person;
    private AddPersonStatus status;
    private AddPersonFailureReason failureReason;
}

enum AddPersonStatus {
    SUCCESS, FAILURE
}

enum AddPersonFailureReason {
    DUPLICATE_EMAIL_ADDRESS,
    DUPLICATE_USER_NAME
}

The view you send the user to can bind to the response object returned by the service and give appropriate feedback.

Paul Morie
Not fond of status codes. I see return values and status codes ignored every time I turn around, resulting in obscure crashes later when the invalid data makes it deep into subsequent business logic.
Jeffrey Hantin
+1  A: 

I think I would choose to throw an exception. You could create a custom exception derived from the Exception class. This way, you are able to use the 'Message' field from your class as an end-user friendly message while still being able to log the actual exception details from the base's 'innerException' field. This way you can choose to hide the ugly details of why your method failed to the user while still maintaining the verbosity of the base's exception description.

turkeyburger
Telling a user that their email address is already in use is not an "ugly detail", it is an easily checked validation step.
Peter J
+1  A: 

This discussion is age-old and you will find different camps. My take on this is that if you have a backend system which has a clear standalone function, you should develop that first, with an API and everything, and then use that API from your view layer. In this case, layering is warranted. One example is that you are writing a web frontend to your already developed custom software.

In most webapps, however, it is much easier and more straight forward to use one single source of configuration. Django, RoR, JBoss Seam etc all take this approach. They use a concept centering around a set of domain classes, where all validation logic and constraints are put. Model exceptions (such as NoSuchObject) gets directly mapped to view exception (404 Not Found), all handled by the framework. With this approaches, there are no layers, and the main idea is to avoid layering until it is really needed. The Django docs are a real good source of information on this topics, and the Seam ones are more technical but also more detailed.

The decision to expose some logic as a web service does not automatically warrant a decision of building a complete layered solution, it is easy to refactor, YAGNI and DRY rule here I would say.

Layering really adds another dimension of problems propagating information back and forth, and I would say that unless it fits your problem well, don't use it.

disown
+1  A: 

I must say that I'm not a huge fan of the "validation errors are exceptions" pattern.

My model Save() methods simply create and return a list of broken rules (or an empty list.) It is then the job of my ControllerBase (or service layer) to handle those broken rules however they see fit.

In the case of my ControllerBase, I add them one by one to the MVC validation handler via AddModelError. In the service layer, it ends up being something like adding a "isValid" flag to my JSON result.

Peter J
Agree totally with your statement regarding validation errors and exceptions. Validation errors should be expected and handled more elegantly than throwing an exception. To those that would disagree, say for example that we're processing 10,000 records on a backend system instead of a single one through a UI. Is it still appropriate to handle a validation error with an exception? I think in this case, you have to make some note in metadata about the failure, since you may have to decide downstream whether to rollback your transaction for something like a failure threshold being reached.
Paul Morie
+1  A: 

There are basically two ways of doing this: exceptions and returning the results of business rules validation. Each way has its advantages and disadvantages, but basically:

Exceptions:

  • you can return only one failed result at the time
  • exceptions are easy to implement and do not complicate your business logic - simply check the condition and throw a business exception
  • exceptions work only for blocking rules
  • open transactions can be easily rolled back

Business rules validation:

  • you can check multiple business rules and return the list of broken ones to the user
  • multiple rules can complicate the flow of your logic, because they tend to mess up the return types from methods
  • they allow more scenarios to be handled, e.g. non-blocking informational rules

I think it heavily depends on the application which approach suits better, so there is no simple answer. For a long time I successfully used exceptions for business rules, now I tend to leverage the second approach.

bbmud
A: 

The approach I use with Spring MVC is to perform all validation within the my Validator class. So from within my validate method I would call a method on the service to check if the email address was already used. If it was a duplicate I would add an error for that binding to the Errors object for showing on the form. If the Person object was valid you should then not need to worry about expections being thrown when it is added to the service.

Mark
I considered this initially...but for some reason I think of validation logic as a step removed from the model. Perhaps because validators are typically called by controllers to validate form inputs. In this case it feels to me like this specific example of avoiding duplicates is a business rule and should be checked at the business layer. Thoughts?
Boden
I guess from looking at all the answers to your question it depends on the context. In my case, the controller is the only place where an object is added to the service so doing the duplicate check in the validator is not a big deal. However, if you say you may provide the business layer via a web service then you would be best checking for duplicates in the business layer as that is all that would get called.
Mark
Boden, your feeling is right that business rules should be checked at the business layer. That way, regardless of how you invoke the Service or Model methods (via MVC controller or Web Service), those rules are being checked.
Peter J