25

I am using FluentValidation to validate my service operations. My code looks like:

using FluentValidation;

IUserService
{
    void Add(User user);
}

UserService : IUserService
{
    public void Add(User user)
    {
       new UserValidator().ValidateAndThrow(user);
       userRepository.Save(user);
    }
} 

UserValidator implements FluentValidation.AbstractValidator.

DDD says that domain layer have to be technology independent.

What I am doing is using a validation framework instead of custom exceptions.

It's a bad idea to put validation framework in the domain layer?

dave1010
  • 15,135
  • 7
  • 67
  • 64
Vinicius Gonçalves
  • 2,514
  • 1
  • 29
  • 54

3 Answers3

56

Just like the repository abstraction?

Well, I see a few problems with your design even if you shield your domain from the framework by declaring an IUserValidator interface.

At first, it seems like if that would lead to the same abstraction strategy as for the Repository and other infrastructure concerns, but there's a huge difference in my opinion.

When using repository.save(...), you actually do not care at all of the implementation from the domain perspective, because how to persist things is not a domain concern.

However, invariant enforcement is a domain concern and you shouldn't have to dig into infrastructure details (the UserValidtor can now be seen as such) to see what they consist of and that's basically what you will end up doing if you do down that path since the rules would be expressed in the framework terms and would live outside the domain.

Why would it live outside?

domain -> IUserRepository
infrastructure -> HibernateUserRepository

domain -> IUserValidator
infrastructure -> FluentUserValidator

Always-valid entities

Perhaps there's a more fundamental issue with your design and that you wouldn't even be asking that question if you adhered to that school of though: always-valid entities.

From that point of view, invariant enforcement is the responsibility of the domain entity itself and therefore shouldn't even be able to exist without being valid. Therefore, invariant rules are simply expressed as contracts and exceptions are thrown when these are violated.

The reasoning behind this is that a lot of bugs comes from the fact that objects are in a state they should never have been. To expose an example I've read from Greg Young:

Let's propose we now have a SendUserCreationEmailService that takes a UserProfile ... how can we rationalize in that service that Name is not null? Do we check it again? Or more likely ... you just don't bother to check and "hope for the best" you hope that someone bothered to validate it before sending it to you. Of course using TDD one of the first tests we should be writing is that if I send a customer with a null name that it should raise an error. But once we start writing these kinds of tests over and over again we realize ... "wait if we never allowed name to become null we wouldn't have all of these tests" - Greg Young commenting on http://jeffreypalermo.com/blog/the-fallacy-of-the-always-valid-entity/

Now don't get me wrong, obviously you cannot enforce all validation rules that way, since some rules are specific to certain business operations which prohibits that approach (e.g. saving draft copies of an entity), but these rules aren't to be viewed the same way as invariant enforcement, which are rules that applies in every scenarios (e.g. a customer must have a name).

Applying the always-valid principle to your code

If we now look at your code and try to apply the always-valid approach, we clearly see that the UserValidator object doesn't have it's place.

UserService : IUserService
{
    public void Add(User user)
    {
       //We couldn't even make it that far with an invalid User
       new UserValidator().ValidateAndThrow(user);
       userRepository.Save(user);
    }
}

Therefore, there's no place for FluentValidation in the domain at this point. If you still aren't convinced, ask yourself how you would integrate value objects? Will you have a UsernameValidator to validate a Username value object everytime it's instanciated? Clearly, that doesn't make any sense and the use of value objects would be quite hard to integrate with the non always-valid approach.

How do we report all errors back when exceptions are thrown then?

That's actually something I struggled with and I've been asking that myself for a while (and I'm still not entirely convinced about what I'll be saying).

Basically, what I've come to understand is that it isn't the job of the domain to collect and return errors, that's a UI concern. If invalid data make it's way up to the domain, it just throws on you.

Therefore, frameworks like FluentValidation will find their natural home in the UI and will be validating view models rather than domain entities.

I know, that seems hard to accept that there will be some level of duplication, but this is mainly because you are probably a full-stack developer like me that deals with the UI and the domain when in fact those can and should probably be viewed as entirely different projects. Also, just like the view model and the domain model, view model validation and domain validation might be similar but serves a different purpose.

Also, if you're still concerned about being DRY, someone once told me that code reuse is also "coupling" and I think that fact is particularly important here.

Dealing with deferred validation in the domain

I will not re-explain those here, but there are various approaches to deal with deferred validations in the domain such as the Specification pattern and the Deferred Validation approach described by Ward Cunningham in his Checks pattern language. If you have the Implementing Domain-Driven Design book by Vaughn Vernon, you can also read from pages 208-215.

It's always a question of trade-offs

Validation is an extremely hard subject and the proof is that as of today people still don't agree on how it should be done. There are so many factors, but at the end what you want is a solution that is practical, maintainable and expressive. You cannot always be a purist and must accept the fact that some rules will be broken (e.g you might have to leak some unobtrusive persistence details in an entity in order to use your ORM of choice).

Therefore, if you think that you can live with the fact that some FluentValidation details makes it to your domain and that it's more practical like that, well I can't really tell if it will do more harm than good in the long run but I wouldn't.

plalx
  • 42,889
  • 6
  • 74
  • 90
  • @plalx, great answer. I see a lot of examples on the web where FluentValidation is used inside domain model. Where domain model knows about its validator and is responsible test itself and throw exception if any issues. It is actually just an extraction of validation logic from domain object to the validator (and potentially share some validation). But if I understand you correctly, you think it isn't worth the hassle? – Tenek Jun 19 '17 at 17:11
  • 4
    @Tenek It's not the extraction of the rules per se that is a hassle (that may even be desirable in some cases), but the dependency of the domain on a third-party library which most of the time does more harm than good. Validation frameworks are most helpful when you must collect & present many errors at the time as well while in the domain you stop at the first sign of trouble because the goal is to protect the integrity of the data and not to guide the user through a process and explain what was wrong. – plalx Jun 19 '17 at 17:31
1

If I understood it correctly, I see no problem whatsoever in doing this as long as it is abstracted as an infrastructure concern just like your repo abstracts the persistence technology.

As an example, I have created in for my projects an IObjectValidator that returns validators by object type, and a static implementation of it, so that I'm not coupled to the technology itself.

public interface IObjectValidator
{
    void Validate<T>(T instance, params string[] ruleSet);

    Task ValidateAsync<T>(T instance, params string[] ruleSet);
}

And then I implemented it with Fluent Validation just like this:

public class FluentValidationObjectValidator : IObjectValidator
{
    private readonly IDependencyResolver dependencyResolver;

    public FluentValidationObjectValidator(IDependencyResolver dependencyResolver)
    {
        this.dependencyResolver = dependencyResolver;
    }

    public void Validate<T>(T instance, params string[] ruleSet)
    {
        var validator = this.dependencyResolver
            .Resolve<IValidator<T>>();

        var result = ruleSet.Length == 0
            ? validator.Validate(instance)
            : validator.Validate(instance, ruleSet: ruleSet.Join());

        if(!result.IsValid)
            throw new ValidationException(MapValidationFailures(result.Errors));
    }

    public async Task ValidateAsync<T>(T instance, params string[] ruleSet)
    {
        var validator = this.dependencyResolver
           .Resolve<IValidator<T>>();

        var result = ruleSet.Length == 0
            ? await validator.ValidateAsync(instance)
            : await validator.ValidateAsync(instance, ruleSet: ruleSet.Join());

        if(!result.IsValid)
            throw new ValidationException(MapValidationFailures(result.Errors));
    }

    private static List<ValidationFailure> MapValidationFailures(IEnumerable<FluentValidationResults.ValidationFailure> failures)
    {
        return failures
            .Select(failure =>
                new ValidationFailure(
                    failure.PropertyName, 
                    failure.ErrorMessage, 
                    failure.AttemptedValue, 
                    failure.CustomState))
            .ToList();
    }
}

Please note that I have also abstracted my IOC container with an IDependencyResolver so that I can use whatever implementation I want. (using Autofac at the moment).

So here is some bonus code for autofac ;)

public class FluentValidationModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        // registers type validators
        builder.RegisterGenerics(typeof(IValidator<>));

        // registers the Object Validator and configures the Ambient Singleton container
        builder
            .Register(context =>
                    SystemValidator.SetFactory(() => new FluentValidationObjectValidator(context.Resolve<IDependencyResolver>())))
            .As<IObjectValidator>()
            .InstancePerLifetimeScope()
            .AutoActivate();
    }
}

The code could be missing some of my helpers and extensions, but I believe it would be more than enough to get you going.

I hope I have helped :)

EDIT:

Since some fellow coders prefer not to use the "service locator anti pattern", here is a very simple example on how to remove it and still be happy :)

The code provides a dictionary property that should be filled with all your validators by Type.

public class SimpleFluentValidationObjectValidator : IObjectValidator
{
    public SimpleFluentValidationObjectValidator()
    {
        this.Validators = new Dictionary<Type, IValidator>();
    }

    public Dictionary<Type, IValidator> Validators { get; private set; }

    public void Validate<T>(T instance, params string[] ruleSet)
    {
        var validator = this.Validators[typeof(T)];

        if(ruleSet.Length > 0) // no ruleset option for this example
            throw new NotImplementedException();

        var result = validator.Validate(instance); 

        if(!result.IsValid)
            throw new ValidationException(MapValidationFailures(result.Errors));
    }

    public Task ValidateAsync<T>(T instance, params string[] ruleSet)
    {
        throw new NotImplementedException();
    }

    private static List<ValidationFailure> MapValidationFailures(IEnumerable<FluentValidationResults.ValidationFailure> failures)
    {
        return failures
            .Select(failure =>
                new ValidationFailure(
                    failure.PropertyName,
                    failure.ErrorMessage,
                    failure.AttemptedValue,
                    failure.CustomState))
            .ToList();
    }
}
RagingKore
  • 31
  • 1
  • 4
  • 1
    From what I read you shouldn't use IOC containers anywhere in your code but at the composition root or the highest possible level. Otherwise, it's still very much like the service locator pattern even, when injecting the locator, because you're still hiding dependencies. You have no way to tell from outside the class what will be asked from the container. Even worse, you could have runtime errors if the container is not properly configured when instanciating objects. – plalx Feb 08 '15 at 15:25
  • @plalx I never agreed with that point of view since I don't expose the IOC container abstraction anywhere besides the Infrastructure layer. I use it as a very powerful tool. But feel free to invert the paradigm and just register the validators in a collection inside your abstraction. – RagingKore Feb 08 '15 at 16:09
  • Thank you for reply RagingKorean. Your solution works fine. The issue of anti-Patter Service Locator is a problem. It would be a bad idea referencing FluentAPI library in my domain class library? I mean, without abstracting it? I would like to keep the simplest things possible without breaking the ease of maintenance. – Vinicius Gonçalves Feb 08 '15 at 16:17
  • I agree with @plax about not use the Service Locator – Vinicius Gonçalves Feb 08 '15 at 16:24
  • 1
    To make it quick, your domain class lib should only contain the entity Model and the only dependencies should be abstractions. Only on your core apps like a service bus or a web API per example you should put things together. That way you would have something like Myproject.Domain, MyProject.Validation.FluentValidation and finally MyProject.Validation.FluentValidation.Autofac. – RagingKore Feb 08 '15 at 16:24
  • Regarding the service locator pattern, I'm confortable using it as tool just like I said before, in the infrastructure tooling. But again, instead of having your IObjectValidator using a "service locator" just create a collection of validators and in the autofac module, resolve them and add them to your IObjectValidator implementation. – RagingKore Feb 08 '15 at 16:28
  • How would be the validation of my User entity from your example? (eg. validating a required userName) – Vinicius Gonçalves Feb 08 '15 at 16:36
  • @ViniciusGonçalves dude thats up to you and has nothing to do with the question itself. but I guess it would be something like `RuleFor(user => user.UserName).NotNull();` – RagingKore Feb 08 '15 at 17:37
  • 1
    @RagingKore I gave my take on the subject, if you are interested. – plalx Feb 08 '15 at 19:00
  • @plalx I have read it and agree it's complicated. But since I do CQRS, I give the power of validation to my Command Handlers, because they know what they need to do and in what state the aggregate should be. Therefore the Validators stay in the same project as the Command Handlers. Then there's the immediate or deferred error handling. Depending on the business needs I either let it go and the command message just stays in the error queue until handled, or raise an error event specific to that command. Have a look: [link](http://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/) – RagingKore Feb 08 '15 at 21:24
  • @RagingKore Well, I just read and that guy doesn't seem to understand much of what always-valid means and what are invariants at all. He's giving silly examples of self-validating entities having implementing an `isValid` method to back up his post, but no ones would do that in DDD... that's ActiveRecord. Command handlers aren't part of the domain and you shouldn't leak business logic there. Also keep in mind that this article is 6 years old... – plalx Feb 08 '15 at 21:41
  • 1
    I just found that Greg had answered that as well... http://codebetter.com/gregyoung/2009/05/22/always-valid/ – plalx Feb 08 '15 at 21:50
1

Answer on your question depends what kind of validation you want put into validator class. Validation can be part of domain model and in your case you've implemented it with FluentValidation and I not see any problems with that. The key thing of domain model - you can use your domain model everywhere, for example if your project contains web part, api, integration with other subsystems. Each module reference to your domain model and works same for all.

vasilyk
  • 142
  • 1
  • 10