使用 FluentValidation 在应用层共享域验证逻辑

Sharing domain validation logic in application layer by using FluentValidation

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

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);
        }
    }

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

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

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

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

问题是;在 FluentValidation 验证器 class 中使用域验证代码的最佳做法是什么?

这两个数字确实在不同的模型中——请求模型和域模型。在重新阅读问题和我原来的答案时,我想我看到了这里的不匹配。

前端验证应限于基本数据类型。如果您需要一个整数,请确保调用者发送的是一个整数,日期是日期,字符串是字符串。这是 Controller 的责任(或任何直接从用户那里接收请求的东西)。如果用户发送了一个无效的整数,它会在这个级别被拒绝。这里没有进行范围检查,我们只检查以确保请求格式正确。

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

域逻辑试图实现用户的愿望并创建一个带有无效编号的新学生。 Domain 决定这不是一个有效的数字,并在这个级别抛出异常,使命令执行失败。控制器检测到失败的命令执行和 returns 对调用者的适当失败(或者,如果命令执行是异步的,则以适当的方式处理错误)。

您最初的直觉是正确的,您违反了 DRY。这可以通过更改业务规则以允许更广泛的范围来显示。这个单一的业务规则更改将需要两次代码更改,从而证明您在重复自己。