0

I am looking at Fluent Validation for my rich domain model: https://github.com/JeremySkinner/FluentValidation

Lets say I have a class called: Customer. The Customer must have a surname. I can do this:

RuleFor(customer => customer.Surname).NotEmpty();

I can do something similar to make sure they are over a certain age etc. Now lets say I want to provide offers to a customer who is between the ages of 18 and 25. Is this something I should be doing using the Fluent API? If not then is there another tool/pattern that I should be using?

All as I need to do is return a message if the person is between those ages i.e. you are entitled to the following offers: x,y, and z". Please note this is for one customer rather than a collection of customers.

Is it normal to test domain rules using Fluent API? For example, if user is between 18-25, then show them this offer (string); if they are 25-40 then show them this offer etc. These are not validation errors i.e. it is perfectly reasonable for someone to be 25 years old or 40 years old etc.

4
  • 1
    Doing this in a fluent API sounds promising to me. What is your question / issue?
    – marstato
    Commented Jun 26, 2017 at 9:47
  • @marstato, please see the last paragraph. Does that help?
    – w0051977
    Commented Jun 26, 2017 at 9:53
  • Fluent Validation would be a good candidate for this use case, but please keep in mind that, were the business rule to change, you would have to recompile and deploy. If your business rules change frequently, this can get painful. Keeping the configuration in an external file would certainly make things easier, but I don't know if any such framework already exists. Fluent Validation does allow you to inject custom code, but I don't know how much flexibility it can offer.
    – s.m
    Commented Jun 26, 2017 at 9:56
  • @s.m. thanks. Is it normal to test business rules using Fluent API (as oppose to validation rules)? For example, if a user is 40 and is not entitled to offers in the 18-25 range, then is it a validation failure?
    – w0051977
    Commented Jun 26, 2017 at 10:00

3 Answers 3

0

I'd make a ValidationProvider class that'll get your validators on demand, given any domain state.

like:

public class UserValidatorProvider
{
    public static IValidator GetValidator(User user)
    {
        if(user.Age > 40){
            return validator1;//you'd probably use the fluent api here
        }else{
            return validator2;//or maybe encapsulate that fluent in a ctor of a class like new UserOver40Validator()
        }
    }
}

That is, a Domain has a State (like, the user is over 40), given a State select a Validator.

You'd have to swap validators at runtime.

As for where to put them in the layers, you will have different validation procedures for each use case at all layers necessary. That is, UI feedback, Business Rules, Domain Validation, but only where needed.

I think it's that simple!

0

The requirements that you have shown just sounds like validations closest to the UI, which is perfectly valid, and is something I usually do.

That is:

  • Use FluentValidation in whatever UI you are using, for fast feedback to the user (and as I call it, to "interrupt" unnecessary calls to the application services/domain code)
  • After these initial validations and checks I'm also validating the command arriving from the UI
  • And the final validations are in my aggregate root class to enforce all the business rules and invariants of the domain model

Yes, this is code duplication and it does violate the DRY principle BUT it is intended. Because these (and other) validations are happening in multiple places.

To answer your comment: yes, such validations, and enforcements of business rules should be in the domain model first of all. I always apply an upside-down approach. After you have mapped the constraints of a given set operations on a model, you should always start implementing the "inside" which is your entity/AR, then head towards "out" the UI.

Also you write in comment:

If the user happens to be 40 years old, then they can still login and get a different set of offers.

This sounds like as a restriction close the UI, since most of the domains I have worked with, did not really have a definition of "login or being logged in" in their common vocalbury, so I think it's an application level responsibility rather than domain.

1
  • Thanks. However, surely this is domain knowledge so should be in the domain model? If the user happens to be 40 years old, then they can still login and get a different set of offers.
    – w0051977
    Commented Jun 26, 2017 at 9:50
0

I'd implement this using criteria that an entity can fulfill or cannot. I'm not veriy familiar with C# so this is really just a rough outline:

// declare your crietria as delegates - in Java this would be Predicate<T>
delegate boolean isEligibleForOfferXYZ(Customer c) {
    return c.age >= 18 && c.age <= 25;
}

You can then use that thing in various contexts. In your UI you can do

if (isEligibleForOfferXYZ(currentUser)) {
    // output button that links to offer order page
}

You can use it as a business rule

if (!isEligibleForOfferXYZ(currentUser)) {
    fail("You are not eligible for this offer, sorry.");
}

You can use it as a validation rule with the fluent API.


You can further extend this, just like Hamcrest matchers:

interface Criterion extends Predicate<T> {
    boolean matches(T item);
    Translation describeMismatch(T item);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.