views:

36

answers:

2

Hi, I am developing application having business objects created from EF 4.0. The application is layered with Data Layer having repository of classes. Above that is a business layer where business processes are mapped as per the requirements. Business layer is calling data layer.

Business objects have complex associations. One scenario: Merchant has Multiple Addresses Merchant belongs to one Category Merchant has an Account Account has a Wallet

User will be creating a new merchant along with above mentioned business objects (graph). There are various business rules when creating and updating a merchant.

I would like to split my validations into two sub sections. 1) Scalar Property Validation to be handled by Validation Blocks 5.0 and 2) Business Process Rules Validation to be handled in business layer components. However, i am facing difficulty on having a centralized way to report broken rules (Scalar Properties validation and Business Process Rule Validation). I don't want to inject exceptions everywhere where business rule in is violated as per the business process mapped.

One e.g is following: When creating a new merchant, Category needs to be validated. Because if a certain type category is associated with this new merchant then business rule says that such merchant cannot exists twice in the system.

Now, when a UI pass the new merchant graph to business layer component, i first validate BO Scalar properties validation (validated bu Validation Blocks) then this BO is passed into business process rule validation methods to check various rules. There are possible violation points which i want to report and shall not allow the merchant and graph objects to be persisted.

Please share any value able design approach to manage centralized validation rule logging and reporting to UI layer.

EDIT: I don't want to incorporate validations on EF SaveChanges, ChangeAssociation etc event handlers.

A: 

Object member validation can be done within encapsulated business objects. Whether you do this in the setter properties or as separate method calls is up to you. Should the objects be set with values that are invalid, should the application allow data into the system that is of the wrong type, range checks etc.

As for the Rules part, I would look at the visitor pattern for each object graph that your trying to achieve some rules checking on. I would report this back probably as a new nested object based on what was found. My personal preference to this reporting side, is to use a visitor pattern that produces an XML Document or some other custom nested class depending on your efficiency needs. The actual rules within the visitor pattern can be declared outside of the visitor pattern, preferably in a declarative approach. For example CheckDuplicateRecord. This would allow for reuse.

Keep all this in the same layer as the Business Layer, but further sub divide the business layer into a Rules validation layer and the Business Objects.

My personal approach with using EF is to use POCO objects as these allow for scalability. I would then do some validation on the UI, then do some validation when transported to the Business Object layer, then do the same again in the EF DAL layer.

WeNeedAnswers
A: 

While you said you don't want to throw exceptions, I found throwing exceptions very effective. Especially when you having multiple sub systems validating the system, throwing one single type of exception as a facade will be very effective.

What you can do is creating a custom exception (i.e. named ValidationException) that will be thrown either when your Validation Application Block (VAB) reports error or the business rules report errors. In the presentation layer you will have to catch this ValidationException and report this exact type of exception in a user friendly way to the end user.

Using a single exception type for both validation sub systems, allows you to report errors in a consistent way, and also ensures that validation errors won't get unnoticed when you (or an other developer) forgets to handle validation errors. Handling errors should be very explicit IMO. When you let methods return a list of errors it is easy to forget to handle them. When creating a custom exception, it is easy to add a property to that exception type that contains a list of validation errors. Such a list of errors is easily extracted from VAB. I don't know what validation system you use for your business rules validation, but it can't be to hard to extract a list of error codes from it.

The most simple way to handle this in UI is of course by using a try-catch:

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

try
{
    businessCommand.Execute();
}
catch (ValidationException ex)
{
   UIValidationHelper.ReportValidationErrors(ex.Errors);
}

Of course having these try-catch statements all over the place is ugly, but at least this code is easy to follow. Dependent on how you structured your business layer and the UI technology you use, there are prettier solutions you could use. For instance, you can wrap the actual operation that can fail with an action as follows:

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

UIValidationHelper.ExecuteOrDisplayErrors(() => 
{
    businessCommand.Execute();
});

The UIValidationHelper would look like this:

public static class UIValidationHelper
{
    public static void ExecuteOrDisplayErrors (Action action)
    {
        try
        {
           action();
        }
        catch (ValidationException ex)
        {
            // Show the errors in your UI technology
            ShowErrorMessage(ex.Errors);
        }
    }
}

An other option, that I've used myself in the past, is by extending the business layer with events. For this to work you need a construct such as commands, as I use in my examples. When using events, the UI could look like this:

var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

businessCommand.ValidationErrorOccurred +=
    UIValidationHelper.DisplayValidationErrors;

businessCommand.Execute();

This example hooks a static method to the ValidationErrorOccurred event of a command instance. The trick here is to let the Execute method of that command catch ValidationExceptions and route them to the ValidationErrorOccurred when it is registered. When no method is registered, this exception should bubble up the call stack, because an unhandled validation exception should of course not get unnoticed.

While it is possible to of course do this directly from your business layer, it would make your business layer dependent on a particular validation technology. Besides this, this method allows clients to choose to handle validation errors in any way they want or decide not to handle them at all (for instance when you don’t expect any validation errors to occur in a particular use case).

When using ASP.NET Web Forms, the DisplayValidationErrors method of the UIValidationHelper could look like this:

public static class UIValidationHelper
{
    public static void DisplayValidationErrors(
        object sender, ValidationErrorEventArgs e)
    {
        Page page = GetValidPageFromCurrentHttpContext();
        var summary = GetValidationSummaryFromPage()

        foreach (var result in e.Error.Results)
        {
            summary.Controls.Add(new CustomValidator
            {
                ErrorMessage = result.Message,
                IsValid = false
            });
        }
    }

    private static Page GetValidPageFromCurrentHttpContext()
    {
        return (Page)HttpContext.Current.CurrentHandler;
    }

    private ValidationSummary GetValidationSummaryFromPage(Page page)
    {
        return page.Controls.OfType<ValidationSummary>().First();
    }
}

This static helper method injects the messages of the reported errors into a ValidationSummary control on the page. It expects the page to contain a ValidationSummary control at root level of the page. A more flexible solution can easily be created.

The last thing that I like to show you is how the the BusinessCommand would look like when adopting this solution. The base class of these commands could look something like this:

public abstract class BusinessCommand
{
    public event EventHandler<ValidationErrorEventArgs>
        ValidationErrorOccurred;

    public void Execute()
    {
        try
        {
            this.ExecuteInternal();
        }
        catch (ValidationException ex)
        {
            if (this.ValidationErrorOccurred != null)
            {
                var e = new ValidationErrorEventArgs(ex);
                this.ValidationErrorOccurred(this, e);
            }
            else
            {
                // Not rethrowing here would be a bad thing!
                throw;
            }
        }
    }

    protected abstract void ExecuteInternal();
}

The ValidationErrorEventArgs looks like this:

public class ValidationErrorEventArgs : EventArgs
{
    public ValidationErrorEventArgs(ValidationException error)
    {
        this.Error = error;
    }

    public ValidationException Error { get; private set; }
}

I hope this all makes sense and sorry for my long answer :-)

Good luck.

Steven
@Steven, thanks for such a detailed answer. Yes, i agree that having customer exception is more appropriate then not throwing exception. Along with having validation rules logging linked to the customer exception is right approach.
Wikki