通过使用FluentValidation在应用程序层中共享域验证逻辑

时间:2018-07-03 02:38:51

标签: c# domain-driven-design fluentvalidation

在我们的域中,我们有Student,每个学生都有名字,姓氏和学生编号。学生编号的业务规则是,学生编号可以是1到99999之间的任何数字。由于此验证属于域层,因此我为此编写了一个类,例如;

public class StudentNumber : ValueObject
    {
        public StudentNumber(int value)
        {
            if (value <= 0 || value > 99999)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(value),
                    "Student number must be in range of (0,9999]");
            }

            Value = value;
        }


        public int Value { get; private set; }

        public static implicit operator StudentNumber(int value)
        {
            return new StudentNumber(value);
        }
    }

但是,最好在应用程序层验证输入。因此,我最终得到了一个验证类(通过使用FluentValidation库),该类可以在下面找到;

public class CreateStudentCommandValidator : AbstractValidator<CreateStudentCommand>
    {
        public CreateStudentCommandValidator()
        {
            RuleFor(m => m.StudentNumber)
                .ExclusiveBetween(0, 100000)
                .WithMessage(ErrorCodes.InvalidStudentNo);
        }
    }

在CreateStudentCommand中,StudentNumber属性的数据类型为整数。

此验证器有效,但是违反了DRY规则。由于我在域层和应用程序中都重复了验证逻辑。

问题是;在FluentValidation验证程序类中使用域验证代码的最佳实践是什么?

1 个答案:

答案 0 :(得分:0)

这两个数字实际上处于不同的模型中-请求模型和域模型。重新阅读问题和原始答案时,我认为我在这里看到了不匹配的地方。

前端验证应限于基本数据类型。如果您希望使用整数,请确保呼叫者发送了整数,日期是日期,字符串是字符串。这是控制器(或直接从用户那里收到请求的任何人)的责任。如果用户发送无效的整数,则在此级别将拒绝该整数。这里没有进行范围检查,我们只是检查以确保请求格式正确。

然后,控制器创建命令。该命令的作用是将用户的需求传达给域逻辑,该域逻辑执行业务逻辑来执行该需求。我们假设用户确实确实想创建一个具有给定编号的学生,因此命令应将此要求传达给域,即命令中不应进行范围验证。通常仅使用适当类型的构造函数参数来完成命令验证。

“域”逻辑尝试执行用户的需求并创建一个具有无效编号的新学生。域确定这不是一个有效的数字,并在此级别上引发异常,从而使命令执行失败。控制器感测到失败的命令执行,并向调用者返回适当的失败(或者,如果命令执行是异步的,则以适当的方式处理错误)。

您最初的直觉是正确的,因为您违反了DRY。可以通过更改业务规则以扩大范围来证明这一点。单个业务规则更改将需要更改两个代码,从而证明您在重复自己。