Problem using F# for validating domain objects in a easy and safe manner

154 views
Skip to first unread message

mblue

unread,
Nov 25, 2015, 2:53:05 AM11/25/15
to F# Discussions
I am new to F# and looking for a way to introduce it in our org. Specifically, I am investigating if I can use F# for easy and safe validation of C# domain objects in a web service. All validations are related to properties on objects. Examples: property must not be null/empty, field max length, field must satisfy regular expression etc.

The special thing here is that the F# code need to BOTH able to perform the validations AND expose the validation rules to the clients of the service, so GUI clients can perform pre-validation themselves by reusing a single set of validation rules (DRY principle applied across service boundaries).

So besides being able to do validation of a domain object and tell of success/failure details, the F# code must also be able to list all possible validations (ultimately in a data structure that  can be converted into JSON). In addition the design must be "safe" - not referring to c# property names as "strings" etc. We should get compilation errors if a C# property is misspelled etc.

A complicated C# design exists where validation rules are classes and LINQ expressions are used to associate validation rules to properties. The LINQ expressions can then be reflected upon from C#, hence making it possible to both perform validation and return the rules themselves in C#. 

So C# can do it (in a complicated way) but the questions is how to do it in F# (and better). I have seen a lot of examples of how to use F# for validation (fsharpforfunandprofit has some nice examples) but I could find none that can both execute validation and return validation rules themselves (as json-compatible structures). 

The code below shows an example of a C# domain class and some very initial and unfinished F# code I have written to explore how to represent things. The F# currently use code quotations where C# use LINQ to associate properties to validation rules in a safe manner. I first thought that I could then simply evaluate the code quotations to perform validation and inspect them to output rules as JSON. However but there appear to be no build-in support for evaluation of code quotations and from what I understand it will also be slow if I do evaluate using a 3rd party library. Hence, it seems the F# solution can not compete with the C# solution. 

What to do ?


C# domain class example:
public class Company
{
  public string Name { get; }
  public string Cvr { get; }
  public Company(string name, string cvr)
  {
     this.Name = name;
     this.Cvr = cvr;
  }
 }

Very raw initial F# code so far:

type Validation = 
  | RequiredField
  | RegEx of string
  | MinLength of uint32
  | MaxLength of uint32

type FieldValue = string -> string

type SingleFieldValidation = FieldValue*Validation

let Validate validation (input:string) =
   match validation with
   | RequiredField -> not (String.IsNullOrEmpty input)
   | RegEx pattern -> input<>null && Regex.Match(input, pattern, RegexOptions.IgnoreCase).Success
   | MinLength min -> input<>null && input.Length>=(int)min
   | MaxLength max -> input=null || input.Length<=(int)max

let personValidations (x:Person) = [(<@ x.FirstName @>, RequiredField),(<@ x.Cpr @>, RegEx "\d{8}") ]

Robert Ream

unread,
Nov 25, 2015, 6:04:29 PM11/25/15
to fsharp-o...@googlegroups.com
In a statically typed functional language the way I like to think of validation is to squint a little and think of your data model as an AST for a language. In an abstract sense a parser for a language is just a function composed of a bunch of other functions that together transform one representation of data into another more appropriately structured representation. All along the way a parser enforces the invariants of this more structured representation, normally outputting useful error messages on any validation failures. So validation is just part of the process of this transformation. So InputModel -> Either<OutputModel, Errors>.

IMO the weakness of using the mainstream (C#, Java, etc.) approach to parsing JSON into types, e.g. using an "Auto-Magix Time Saving Reflection Based JSON Library EVERYONE Uses So It Must Be A GOOD THING!", is that you lose any ability to safely and simply enforce invariants in your model. In the default mode most magic JSON libraries are allowed to stuff any old data in the model that it pleases. The consequence is that you are left with a half complete "domain model" that you then must validate and enforce invariants ex-post. So you go from RawBytes -(JSON Magix)-> HalfBakedModel -(Validation)-> HalfBakedModel (but it passed validation... really, honestly it did!). An approach that leverages the type system better would be RawBytes -(Json Parser)-> Either<JsonDataModel, Errors> -(Domain Parser For Json)-> Either<RealDealDomainModel, Errors>.

Of course, this will take more code to write a parser for your domain models. A compromise might be to go RawBytes  -(JSON Magix)-> HalfBakedResourceModel -(Domain Parser For HalfBakedResourceModel)-> Either<RealDealDomainModel, Errors>.

For some ideas on how to better leverage the type system to enforce invariants in F# see: http://fsharpforfunandprofit.com/category/DDD/

Sorry for any grammar/spelling mistakes and if this doesn't make much sense. I wrote this in haste.



--
--
To post, send email to fsharp-o...@googlegroups.com
To unsubscribe, send email to
fsharp-opensou...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/fsharp-opensource
---
You received this message because you are subscribed to the Google Groups "F# Discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fsharp-opensou...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Isaac Abraham

unread,
Nov 27, 2015, 2:00:43 PM11/27/15
to fsharp-o...@googlegroups.com

The Chessie project might be of use to you here as a generalised framework for error handling – look at http://fsprojects.github.io/Chessie/railway.html for an example of how you can use it to quickly and easily compose multiple validation rules together.

 

Of course this might not be exactly what you’re after, but it’s probably a good starting point.

 

Regarding your validation rules as a discriminated union – I suspect you might find it better defining a signature for what a validation rule looks like and simply have a collection / map of them rather than a static set of DU cases.

--

Reply all
Reply to author
Forward
0 new messages