Ron Jacobs

Windows Workflow Foundation

WCF: 4 Tenets of Service Oriented Data Validation

WCF: 4 Tenets of Service Oriented Data Validation

  • Comments 7

Remember the 4 tenets of SOA?  One of them is that Boundaries are explicit.  When somebody sends data to your service it is just like when you cross an international border into another country.  Just a couple of hours drive north of us in Redmond is the border crossing to Canada.  When you cross into Canada or back into the United States you have to stop your car and the border agents do their job.  Their job is to make sure that you have proper documentation and that you aren’t smuggling something (or someone) bad into the country.

Your service has to have a similar border checkpoint and it is at the trust boundary where data enters your “country”.  At the boundary you have to validate the data before it gets down deep into your business logic or database in some invalid form.  The question I want to focus on here is one of design.  Where should the validation be done?

Download Sample Code – WCF Service Fault and Validation Example

For Workflow Services see endpoint.tv - WF4 Workflow Service Data Validation Design

Service Operation

Most of the time we tend to validate data on entry to the service method.  In small applications this approach is manageable but suppose you have a number (call it a Foo) that you use in 15 different services and you always pass it as an integer.  As you review the system you find that some services reject any negative Foo value while others reject any Foo value less than 1.  Your refactoring instinct tells you that it would be a good idea to centralize the Foo validation logic so you don’t end up with a variety of different validation rules.

Take a look at this code.  It works, but could it be better?

public bool SomeOperation(int foo)
{
    if (foo < 0)
    {
        throw new FaultException("Invalid Foo");
    }
 
    return ProcessTheFoo(foo);
}

The problem with this code is that

  1. The validation rule (foo must be greater than zero) is contained within this service method.  If you use foo in another method or another service where it has the same semantic you have to be sure that your validation rule is followed there are well.  Once you code the rule in more than one place you have a system that is difficult to maintain and prone to inconsistent validation.
  2. The response to failed validation is also contained within the service method.  In this case you throw a FaultException but there are many options for how to Fault the service (as I mentioned in my previous post) and once again the way in which you respond to the failure of validation now becomes spread across your codebase resulting in a fragile system.

Ron’s 4 Tenets of Service Oriented Data Validation

Services have to consume and receive data.  This data flows across the service boundary and therefore must be untrusted until validated.  I’m proposing some new tenets for service orientation.  These tenets describe validation rules.  Validation rules are an expression that tells you if the data is valid or not.

  1. Validation Rules should be in one place
  2. Validation Rules should apply to all layers of the app
  3. Validation Rule violations should result in internal exceptions which may cause external faults
  4. Validation Rules may be shared between sender and receiver

Validation Rules should be in one place

In your system you have to validate the state of an object.  The validation rules for object should be written once and only once.  This makes your system more maintainable..

Validation Rules should apply to all layers of the app

Service Oriented Applications consist of the service boundary and lower layers or business logic.  What makes an object valid at one layer should be the same as what makes it valid at another layer.  Lower layers of the system may use internal types which hold data in intermediate states that is not valid according to the rules.  When this is the case you should think of these types as being fundamentally different than the type that the validation rules apply to.

Validation Rule violations should result in internal exceptions which may cause external faults

If validation is called from internal service logic, validation rules should throw exceptions types appropriate for internal use such as ArgumentNullException or ArgumentOutOfRangeException.  At the service boundary if you want to propagate the error to the sender these exceptions must be converted to FaultException or FaultException<TDetail>

Validation Rules may be shared between sender and receiver

If the sender and receiver are able and willing to accept the tighter coupling that comes from sharing assemblies, you can share validation rules between sender and receiver.  If you share validation rules, the sharing should be limited to only the types exposed at the boundary

Scenario: Self-Validating Request Message

Given

  • an entity named Foo which implements the IFoo interface
public interface IFoo
{
    int Data { get; set; }
    string Text { get; set; }
}
  • a DataContract type named GetDataRequest with a property named Foo of type IFoo
  • A class Foo that implements IFoo and does data validation in the property setter
  • A class FooValidator that validates data on behalf of Foo
  • An InternalFoo class that implements internal business logic

When

  • The service is invoked with invalid data

Then

  • WCF deserializes the message body and creates an instance of Foo
  • The Foo property setters run validation code using the FooValidator
  • the FooValidator methods throw ArgumentException
  • and the Foo class converts the ArgumentException to a FaultException<TDetail> and throws
  • The sender catches a FaultException<TDetail>

Conclusion

Sound complex?  Sure… but it is one thing to build a simple example and quite another to show an architecture style that yields some significant benefits.  Of course there are many ways to accomplish these goals – you might have a better way – if so, please share it with me.

Happy Coding!

Ron
http://blogs.msdn.com/rjacobs
Twitter: @ronljacobs

  • Hi Ron,

    There are some great ideas here.

    RE: Validation Rule violations should result in internal exceptions which may cause external faults

    Absolutely agree with this. You should look at leveraging an IErrorHandler for exception translation. The HandleError allows you to log exceptions on a background thread while the ProvideFault allows for exception translation. ProvideFault also plays an important role by allowing you to provide exception shielding (catch all, then return generic fault) to avoid security sensitive information being returned. I have one at jabiru.codeplex.com/.../66884 for example.

    I hit an edge case where I needed to use the IErrorHandler (in the previous link) to essentially manage business validation. It was where WF content correlation failed to find an existing workflow service instance for the data provided to the service. The IErrorHandler was the only place that the thrown exception could be translated into a business fault. See www.neovolve.com/.../Managing-content-correlation-failures.aspx for the details.

    The IErrorHandler can be hooked up via configuration (www.neovolve.com/.../implementing-ierrorhandler.aspx) or as an attribute on the service implementation class (www.neovolve.com/.../Strict-IErrorHandler-usage.aspx). I have provided support for both these scenarios in my toolkit (neovolve.codeplex.com/.../19004).

    RE: Validation Rules may be shared between sender and receiver

    I like this idea, but I prefer to keep data contracts lean and for them not to have any logic. They are essentially just DTO classes. A way to support this idea would be to provide a Validate extension method that is bundled up in the service contract assembly. If you are sharing the service contract binary with clients then they can use the same validation as the service. This could be a blessing and a curse. The validation logic will be versioned along with the service contract assembly, but the client may be using an old service contract version with a newer version of the service. Validation logic may get out of sync in this case.

    On a side note, a good idea is to describe business faults using combinations of code/description. Ideally, the business fault will be able to describe a collection of these code/description failures to avoid unnecessary round-trips for multiple input field failures on the client.

    Failure codes are great because they are human and culture independent (prefer enums for this purpose). A client application can use the code to determine an automated action (retry) or to highlight a failure in a UI with particular input fields. Codes will also allow a client to provide its own culture aware description as required. I have an example of one of these business faults at jabiru.codeplex.com/.../66884.

    Not using codes forces the client to parse fault description strings which is very risky. The only other alternative is to create a fault contract/failure type which will be very messy on the service contracts and difficult for clients to support.

    I have put together an example of how to provide support for all of this with WF custom activities at www.neovolve.com/.../Custom-Workflow-activity-for-business-failure-evaluatione28093Wrap-up.aspx.

  • Thanks Rory - great ideas all around...

  • Hi Ron,

    A common problem that many developers have to face is the distribution of the validation logic between the client and the service. Questions like, "Shall I run this logic on the client, service or both ?", or "How do I do to share this validation logic between the client and the service ?". The common instinct tell you that everything should be validated on the service, but then you start running in some usability issues on the client. For instance, If the service required five fields, and the client only sent two, how do I show that to the user in a friendly way ?. I can not implement the common validation method that shows the "red" boxes around the required fields without moving some validation logic to the client for that scenario.   ASP.NET MVC for example introduced the model, and you can decorate it with data validation annonations so the same model (and validation logic) is shared between the view and the controller. However, the same thing does not apply quite well to services. You can not expect that clients and services will share the same data contracts with the validation logic as you might have other legacy or service stacks consuming your services.

    Pablo.

  • @Pablo - if you provide a Validator class from the service side that can validate the individual data items as well as the entity as a whole and share the assembly that might work.

    For example, a CustomerValidator might have CustomerValidator.ValidateName, ValidateAddress etc.  Then it can have a ValidateCustomer(customer) which could validate as a whole.

  • Hi Ron and Rory,

    I really like the article and Rory's additional remarks as you sum best practices regarding validation and exception handling which I use and propagate since years (DTO, Exception Shielding, Fault Contracts, Security Facades). I have the impression, that the impact of missing validation and insufficient exception handling on the maintainability of software systems is greatly underestimated.

    Just some short remark to Ron's idea mentioned above: We have used this approach in the past to share simple validation logic between client and the server side.

    But how does this approach fit to the tenet "Services share schema and contract, not class" and the pattern "Document Processor" (see msdn.microsoft.com/.../ms954638.aspx)? Following the "Document Processor" pattern, I would suggest to integrate the simple validation rules into the XSchema and share these rules as part of the contract.

    Do you agree?

    Best Regards,

    Martin

  • Hi Ron and Rory,

    I really like the article and Rory's additional remarks as you sum best practices regarding validation and exception handling which I use and propagate since years (DTO, Exception Shielding, Fault Contracts, Security Facades). I have the impression, that the impact of missing validation and insufficient exception handling on the maintainability of software systems is greatly underestimated.

    Just some short remark to Ron's idea mentioned above: We have used this approach in the past to share simple validation logic between client and the server side.

    But how does this approach fit to the tenet "Services share schema and contract, not class" and the pattern "Document Processor" (see msdn.microsoft.com/.../ms954638.aspx)? Following the "Document Processor" pattern, I would suggest to integrate the simple validation rules into the XSchema and share these rules as part of the contract.

    Do you agree?

    Best Regards,

    Martin

  • Document Processor - yes I'm quite familiar with it... I came up with it.

    As I said in the video, some people are not comfortable with the tight coupling that comes from sharing validation logic between client and server.  That is why I said services "may" share validation rules.  

    I say this as a compromise because over the years I've become less dogmatic about SOA and more practical.  I find that many people are creating services that are consumed by other parts of their application.  And in such cases they are often already sharing code between sender and receiver so if you are comfortable with it... share the validation as well.  Just recognize that sharing the validation logic does create a tighter coupling.

Page 1 of 1 (7 items)