Latest Replies
Thursday
Dec042008

Extending Lokad Rules for better .NET Domain Driven Development Experience

Check out this rule declaration for the Lokad Validation and Business Rules Application Block:

public static void ValidateIdentity(Identity identity, IScope scope)
{
  scope.Validate(identity.Name, "Name", StringIs.ValidEmail);
  scope.Validate(identity.Pass, "Pass", StringIs.Limited(3,64));
}

Almost everything is sticking to the DRY principle here (with the ability to shape the validation in any way, since that's plain code). Well, everything except for one thing - name of the property being checked. We duplicate it as a string literal in order to get the proper error path in the description (this is really useful for scenarios when rules, as well as the objects being checked, have deep nesting).

This yields following limitations:

  • Extra typing is needed to define such rules.
  • Refactoring class by renaming the property will make the rule out of the sync with the object. This type of problem is called dormant, since it is not found by the compiler or simple unit tests.
  • This will become quite a problem, if we want to reuse our existing rules at the UI level for the validation (Windows.Forms, AspNet.Forms etc).

There are three obvious solutions to the problem via some kind of Rules DSL:

  1. Implement custom rule declaration syntax in Boo
  2. Wait till C# gets compiler extensibility
  3. Use slightly different declaration syntax

The third option could be implemented in less than an hour and, thus, is a bit more preferable.

Alternative syntax to compose rule for validating the domain objects is:

public static readonly Rule<Identity> ValidIdentity = 
  Validate<Identity>
    .Property(i => i.Name, StringIs.ValidEmail)
    .Property(i => i.Pass, StringIs.Limited(3,64));

As you can see, we mention the member only once here. Yet, due to some C# magic, the name of the property will be derived properly.

This approach has another slight advantage. It is more easy for the ReSharper to figure out the situation in some cases, since we are using strongly-typed delegates instead of the method groups.

The performance impact is negligible, since the code flow does not perform any compilation or reflection after the rule has been created.

This syntax is not in the Validation and Business Rules Application Block for .NET, yet. It will require a bit of usage validation against production scenarios, first. But it should be there quite soon, since leveraging existing rules at the UI level, makes the code more reliable and efficient.

Implementation

Here's the C# code to have this done on top of the latest Lokad.Shared.dll:

// Lokad.Shared.Test/Rules/Case2/RuleSyntax.cs
public static class Validate<T>
{
  public static RuleSyntax<T> Property<P>(
    Expression<Func<T, P>> expression, params Rule<P>[] rules)
  {
    return new RuleSyntax<T>().Property(expression, rules);
  }
}

public sealed class RuleSyntax<T> : Syntax<ICollection<Rule<T>>>
{
  internal RuleSyntax() : base(new List<Rule<T>>())
  {
  }

  public RuleSyntax<T> Property<P>(
    Expression<Func<T, P>> expression, params Rule<P>[] rules)
  {
    Enforce.Argument(expression, "expression");
    var memberExpression = expression.Body as MemberExpression;
    Enforce.That(memberExpression != null, 
      "Expression should be a member expression");
    var member = memberExpression.Member;
    Enforce.That(member.MemberType == MemberTypes.Property, 
      "Expression should be a property reference");

    var getValue = expression.Compile();
    var name = member.Name;

    Target.Add((t, scope) => scope.Validate(getValue(t), name, rules));
    return this;
  }

  public static implicit operator Rule<T>(RuleSyntax<T> syntax)
  {
    var rules = syntax.Target.ToArray();
    return (t, scope) => rules.ForEach(rule => rule(t,scope));
  }
}

The snippet above features:

  • Usage of Linq Expressions
  • Usage of some helper extensions defined in the System namespace of the library
  • Usage of the Syntax{T} class (from the Lokad Shared Libraries) that helps in defining Fluent APIs

So what do you think about this type of syntax? Any feedback or comments are appreciated.

« How to Use SystemUtil to Write .NET Code With Testable Sleep and Time Calls | Main | Top 10 Articles on xLim 2 »

Reader Comments (8)

Yeah I''m using the Linq Expressions based approach too, like it a lot.

December 29, 2008 | Unregistered CommenterColin Jack

This doesn't seem to be checked in as far as I can see, atleast I can't find RuleSyntax

January 14, 2009 | Unregistered CommenterColin Jack

Oops re-reading the end just realized you weren't saying it was checked in, will give it a try and see what I think! :P

January 14, 2009 | Unregistered CommenterColin Jack

Yep, RuleSyntax is not in the trunk. So far this syntax still did not get enough use cases to justify its usage and make the codebase more complex. Basically, that was probably an attempt of premature optimization))

BTW, if you feel that using syntax is worth it in your scenario, you can use Express{T}.Property(expression) to get to the lambda without bothering about the expressions (System.Reflection.Express, Lokad.Shared.dll).

January 14, 2009 | Registered CommenterRinat Abdullin

PS: extensions to bind properties to the rules via expressions (without the syntax) are coming to the trunk later on this evening.

January 14, 2009 | Registered CommenterRinat Abdullin

Unrelated to the post but noticed that Errors.Argument<T> passes arguments to ArgumentException in the wrong order:

return new ArgumentException(paramName, message);

January 14, 2009 | Unregistered CommenterColin Jack

Excellent on changes to trunk, I'm still trying to get my head around how best to use the library though.

Let me explain what I'm trying to do and you can see if you think its a good use case. Validation will be enforced through the domain, no shock.

However I want to extract that validation and use it, pretty directly, in my view/presentation models. So if I had an Address value object I'd want to generate an associated Address presentation model class that would differ in that it would have a default constructor, public setters on the properties and would have an IsValid method.

Anyway I want to code-gen the presentation model classes, for value objects at least. As part of that I want to either:

1) Get rules from the domain objects and copy them to the presentation model class, obviously redirecting them to the properties on the view model.
2) At run-time call a static method on the value object to get the rules to run (e.g. calling Address.GetSimpleRules) and then apply those rules to the presentation model.

I'm in two minds about whether this will work but also about whether I'd be better creating my own validation framework to support it or try to leaverage your own one.

So I'm wondering, do you think what I'm trying to do would be possible using Lokad?

January 14, 2009 | Unregistered CommenterColin Jack

Thank you for pointing out the arguments issue. Nice catch!

Regarding UI-level binding - I've created a sample for you that demonstrates one of the approaches to bind rules to the UI in a DD and strongly-typed manner. Additionally it attempts to avoid duplicating data objects and rules.

Details are here.

I'd love to hear what you think about it. Does it make implementing your scenario simpler?

January 15, 2009 | Registered CommenterRinat Abdullin
Comments for this entry have been disabled. Additional comments may not be added to this entry at this time.