Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web application's form validation - design to propagate domain errors to client-side?

Data validation should occur at the following places in a web-application:

  • Client-side: browser. To speed up user error reporting
  • Server-side: controller. To check if user input is syntactically valid (no sql injections, for example, valid format for all passed in fields, all required fields are filled in etc.)
  • Server-side: model (domain layer). To check if user input is domain-wise valid (no duplicating usernames, account balance is not negative etc.)

I am currently a DDD fan, so I have UI and Domain layers separated in my applications.

I am also trying to follow the rule, that domain model should never contain an invalid data.

So, how do you design validation mechanism in your application so that validation errors, that take place in the domain, propagate properly to the client? For example, when domain model raises an exception about duplicate username, how to correctly bind that exception to the submitted form?

Some article, that inspired this question, can be found here: http://verraes.net/2015/02/form-command-model-validation/

I've seen no such mechanisms in web frameworks known to me. What first springs into my mind is to make domain model include the name of the field, causing exception, in the exception data and then in the UI layer provide a map between form data fields and model data fields to properly show the error in it's context for a user. Is this approach valid? It looks shaky... Are there some examples of better design?

like image 923
Vladislav Rastrusny Avatar asked Nov 11 '15 12:11

Vladislav Rastrusny


2 Answers

Although not exactly the same question as this one, I think the answer is the same:

Encapsulate the validation logic into a reusable class. These classes are usually called specifications, validators or rules and are part of the domain.

Now you can use these specifications in both the model and the service layer.

If your UI uses the same technology as the model, you may also be able to use the specifications there (e.g. when using NodeJS on the server, you're able to write the specs in JS and use them in the browser, too).

Edit - additional information after the chat

  • Create fine-grained specifications, so that you are able to display appropriate error messages if a spec fails.
  • Don't make business rules or specifications aware of form fields.
  • Only create specs for business rules, not for basic input validation tasks (e.g. checking for null).
like image 61
theDmi Avatar answered Sep 27 '22 16:09

theDmi


I want to share the approach used by us in one DDD project.

  • We created a BaseClass having fields ErrorId & ErrorMessage.
  • Every DomainModel derive from this BaseClass & thus have a two extra fields ErrorId & ErrorMessage available from BaseClass.

  • Whenever exception occurs we handle exception(Log in server, take appropriate steps for compensating logic & fetch User Friendly message from client location based localized Resource file for message ) then propagate data as simple flow without raising or throwing exception.

  • At client side check if ErrorMessage is not null then show error.

It's basic simple approach we followed from start of project.

If it's new project this is least complicated & efficient approach, but if you doing changes in big old project this might not help as changes are big.

For validation at each field level, use Validation Application Block from Enterprise Library.

It can be used as :

Decorate domain model properties with proper attributes like:

public class AttributeCustomer 
{
    [NotNullValidator(MessageTemplate = "Customer must have valid no")]
    [StringLengthValidator(5, RangeBoundaryType.Inclusive, 
        5, RangeBoundaryType.Inclusive, 
        MessageTemplate = "Customer no must have {3} characters.")]
    [RegexValidator("[A-Z]{2}[0-9]{3}", 
    MessageTemplate = "Customer no must be 2 capital letters and 3 numbers.")]
    public string CustomerNo { get; set; }
}

Create validator instance like:

Validator<AttributeCustomer> cusValidator = 
            valFactory.CreateValidator<AttributeCustomer>();

Use object & do validation as :

customer.CustomerNo = "AB123";
customer.FirstName = "Brown";
customer.LastName = "Green";
customer.BirthDate = "1980-01-01";
customer.CustomerType = "VIP";

ValidationResults valResults = cusValidator.Validate(customer);

Check Validation results as:

if (valResults.IsValid)
{
    MessageBox.Show("Customer information is valid");
}
else
{
    foreach (ValidationResult item in valResults)
    {
        // Put your validation detection logic
    }
}

Code example is taken from Microsoft Enterprise Library 5.0 - Introduction to Validation Block This links will help to understand Validation Application Block:

http://www.codeproject.com/Articles/256355/Microsoft-Enterprise-Library-Introduction-to-V

https://msdn.microsoft.com/en-in/library/ff650131.aspx

https://msdn.microsoft.com/library/cc467894.aspx

like image 32
Pranav Singh Avatar answered Sep 27 '22 15:09

Pranav Singh