使用 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。这可以通过更改业务规则以允许更广泛的范围来显示。这个单一的业务规则更改将需要两次代码更改,从而证明您在重复自己。
在我们的域中有 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。这可以通过更改业务规则以允许更广泛的范围来显示。这个单一的业务规则更改将需要两次代码更改,从而证明您在重复自己。