值对象是否应该包含对输入参数的技术验证?
Should Value Objects Contain Technical Validation For Input Parameters?
正如 DDD 从业者所建议的,业务规则的验证必须在域对象(实体、值对象和域服务)内部实现,并且在我读过的某个地方也遵循它们自己的上下文,我们应该进行技术验证(例如检查长度,正确的输入格式,正确的数据类型,...)在域模型之外和应用层之类的地方以保持域对象清晰。
现在
我的问题是:
如果我们有一个信用卡号的值对象,我们是否仍然应该对我们的值对象进行技术验证?
换句话说,当我们处理值对象时,“自我验证”一词不涉及技术验证?
当错误的借记卡号码甚至电子邮件地址有可能违反业务规则时,怎么办?
为了更清楚,请注意这个代表借记卡号的值对象:
public class DebitCardNumber : ValueObject
{
public string Number { get;private set; }
public CreditCardNumber(string number)
{
Validation(number);
this.Number = number;
}
private void Validation(string number)
{
if (String.IsNullOrWhiteSpace(number))
{
throw new CardNumberCanNotBeEmptyException();
}
if (number.Length != 16)
{
throw new CardNumberLengthMustBeSixteenDigitException();
}
int sum = 0;
for (int i = 1; i <= 16; i++)
{
if (!char.IsDigit(number[i - 1]))
{
throw new ValueContainsSomeNonDigitCharacterException();
}
int m = (i % 2 == 0 ? 1 : 2);
int a = (int.Parse(number[i - 1].ToString()) * m);
while (a > 9)
{
a -= 9;
}
sum += a;
}
if ((sum % 10) > 0)
{
throw new ValueIsNotCorrectAsACardNumberException()
{ Message = "Perhaps some digits has been entered in wrong order or they are incorrect." };
}
}
}
根据这段代码,有一个验证方法可以执行算法来确定卡号的格式是否正确?
您认为它适合进行此类验证吗?
在我看来你的方法在大多数情况下都是正确的。
如果该号码无效,则它不是真正的借记卡号码。
假设您有一个未经验证的借记卡号码。要么您将布尔值 valid
设置为 false,要么您的代码在骗您:它不是借记卡号码。
如果您仍想存储该数字,可能出于安全或用户体验目的,您应该在不同的对象中执行此操作,也许在输入的表单值对象中。
我不认为您的示例是技术验证规则 - 我认为这是借记卡的域规则 - 如果在银行领域,借记卡号必须遵循特定模式,那么这是需要强制执行的域规则。
所以我认为你的解决方案是正确的。
as DDD practitioner advise, business rule's validations must be implemented inside domain objects (Entities,Value Objects and Domain Services)
是的。
also somewhere I'd read that we should put technical validations (such as check length,correct input formats,correct data type,...) out of domain model and somewhere like application layer to keep domain object clear.
这里有点糊涂;要点是实体不需要担心一堆输入验证,那不是他们的工作(职责分离)。因此,我们不是将原始数据(字符串、原语)传递给我们模型中的实体,而是首先使用原语构造实体将识别的值类型,然后将这些实体传入。
可用于创建格式良好的值类型的原语规则已在值类型本身(构造函数)或为此目的提供的专用工厂中正确实施)。应用程序组件有责任在将值传递给模型之前根据收到的 message/DTO 创建值类型。
因此在您的示例中,DebitCard 验证逻辑看起来是在正确的位置。
注意事项 - 您的模型会随着时间的推移而演变;当模型发生变化时,您仍然需要能够读取模型早期版本写入的数据。添加将当前数据视为无效的验证规则可能会变得混乱 - 因此您要确保验证规则具有业务动机。您是否通过确保借记卡号具有有效的校验和来节省 money/cutting 成本?
(示例:假设客户提交了一张带有无效卡号的采购订单。企业是想拒绝该订单,还是接受该订单但推迟对其采取行动,直到提供了有效的付款方式?如果是后一种选择,您需要确保您的验证逻辑不会妨碍接受订单。
if we had a Value Object for credit card number and so on,still we
should keep technical validation out of our Value Object? in other
words the "Self Validated" term is not involved with technical
validations when we deal with Value Objects?
正确。域(实体、域服务、vos 等)应该被建模,以便它们执行 业务 规则,而不是技术问题。您的域可能需要区分借记卡和信用卡,但我怀疑企业是否关心卡号本身的格式。卡号的格式和正确性对于基础架构而言很重要,因此可以在该层强制执行格式规则。
正如 DDD 从业者所建议的,业务规则的验证必须在域对象(实体、值对象和域服务)内部实现,并且在我读过的某个地方也遵循它们自己的上下文,我们应该进行技术验证(例如检查长度,正确的输入格式,正确的数据类型,...)在域模型之外和应用层之类的地方以保持域对象清晰。
现在 我的问题是:
如果我们有一个信用卡号的值对象,我们是否仍然应该对我们的值对象进行技术验证? 换句话说,当我们处理值对象时,“自我验证”一词不涉及技术验证?
当错误的借记卡号码甚至电子邮件地址有可能违反业务规则时,怎么办?
为了更清楚,请注意这个代表借记卡号的值对象:
public class DebitCardNumber : ValueObject
{
public string Number { get;private set; }
public CreditCardNumber(string number)
{
Validation(number);
this.Number = number;
}
private void Validation(string number)
{
if (String.IsNullOrWhiteSpace(number))
{
throw new CardNumberCanNotBeEmptyException();
}
if (number.Length != 16)
{
throw new CardNumberLengthMustBeSixteenDigitException();
}
int sum = 0;
for (int i = 1; i <= 16; i++)
{
if (!char.IsDigit(number[i - 1]))
{
throw new ValueContainsSomeNonDigitCharacterException();
}
int m = (i % 2 == 0 ? 1 : 2);
int a = (int.Parse(number[i - 1].ToString()) * m);
while (a > 9)
{
a -= 9;
}
sum += a;
}
if ((sum % 10) > 0)
{
throw new ValueIsNotCorrectAsACardNumberException()
{ Message = "Perhaps some digits has been entered in wrong order or they are incorrect." };
}
}
}
根据这段代码,有一个验证方法可以执行算法来确定卡号的格式是否正确? 您认为它适合进行此类验证吗?
在我看来你的方法在大多数情况下都是正确的。
如果该号码无效,则它不是真正的借记卡号码。
假设您有一个未经验证的借记卡号码。要么您将布尔值 valid
设置为 false,要么您的代码在骗您:它不是借记卡号码。
如果您仍想存储该数字,可能出于安全或用户体验目的,您应该在不同的对象中执行此操作,也许在输入的表单值对象中。
我不认为您的示例是技术验证规则 - 我认为这是借记卡的域规则 - 如果在银行领域,借记卡号必须遵循特定模式,那么这是需要强制执行的域规则。
所以我认为你的解决方案是正确的。
as DDD practitioner advise, business rule's validations must be implemented inside domain objects (Entities,Value Objects and Domain Services)
是的。
also somewhere I'd read that we should put technical validations (such as check length,correct input formats,correct data type,...) out of domain model and somewhere like application layer to keep domain object clear.
这里有点糊涂;要点是实体不需要担心一堆输入验证,那不是他们的工作(职责分离)。因此,我们不是将原始数据(字符串、原语)传递给我们模型中的实体,而是首先使用原语构造实体将识别的值类型,然后将这些实体传入。
可用于创建格式良好的值类型的原语规则已在值类型本身(构造函数)或为此目的提供的专用工厂中正确实施)。应用程序组件有责任在将值传递给模型之前根据收到的 message/DTO 创建值类型。
因此在您的示例中,DebitCard 验证逻辑看起来是在正确的位置。
注意事项 - 您的模型会随着时间的推移而演变;当模型发生变化时,您仍然需要能够读取模型早期版本写入的数据。添加将当前数据视为无效的验证规则可能会变得混乱 - 因此您要确保验证规则具有业务动机。您是否通过确保借记卡号具有有效的校验和来节省 money/cutting 成本?
(示例:假设客户提交了一张带有无效卡号的采购订单。企业是想拒绝该订单,还是接受该订单但推迟对其采取行动,直到提供了有效的付款方式?如果是后一种选择,您需要确保您的验证逻辑不会妨碍接受订单。
if we had a Value Object for credit card number and so on,still we should keep technical validation out of our Value Object? in other words the "Self Validated" term is not involved with technical validations when we deal with Value Objects?
正确。域(实体、域服务、vos 等)应该被建模,以便它们执行 业务 规则,而不是技术问题。您的域可能需要区分借记卡和信用卡,但我怀疑企业是否关心卡号本身的格式。卡号的格式和正确性对于基础架构而言很重要,因此可以在该层强制执行格式规则。