Should I abstract the validation framework from Domain layer?

后端 未结 3 1969
猫巷女王i
猫巷女王i 2020-11-27 03:59

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

using FluentValidation;

IUserService
{
    void Add(User user);
}

UserSe         


        
3条回答
  •  一生所求
    2020-11-27 04:31

    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 instance, params string[] ruleSet);
    
        Task ValidateAsync(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 instance, params string[] ruleSet)
        {
            var validator = this.dependencyResolver
                .Resolve>();
    
            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 instance, params string[] ruleSet)
        {
            var validator = this.dependencyResolver
               .Resolve>();
    
            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 MapValidationFailures(IEnumerable 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())))
                .As()
                .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();
        }
    
        public Dictionary Validators { get; private set; }
    
        public void Validate(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 instance, params string[] ruleSet)
        {
            throw new NotImplementedException();
        }
    
        private static List MapValidationFailures(IEnumerable failures)
        {
            return failures
                .Select(failure =>
                    new ValidationFailure(
                        failure.PropertyName,
                        failure.ErrorMessage,
                        failure.AttemptedValue,
                        failure.CustomState))
                .ToList();
        }
    }
    

提交回复
热议问题