我在伪代码中有一个简单的实体 Order
:
class Order{
private int quantity;
private Date orderDate;
private Date shippingDate;
public Order(int quantity, Date orderDate, Date shippingDate){
if(quantity <= 0){ throw new Exception("Invalid quantity")}
if(shippingDate < orderDate){ throw new Exception("Invalid shippingDate")}
if(...more validation...){....throw Exceptions...}
//assign values if everything is OK
}
}
description,quantity,orderDate和shippingDate都是从Web表单中读取的,其中每个表单都是由多个验证器配置的文本字段:
quantityField= new TextField('txt_quantity');
quantityFiled.addNotNullValidator().addNumaricValidator().addPositiveIntegerValidator()
如您所见,验证逻辑在TextField
验证和实体验证之间重复
我试图通过创建Quantity
类,OrderDate
类和ShippingDate
类来向我的实体引入值对象的概念。所以我的Order
实体变成了这样:
class Order{
private Quantity quantity;
private OrderDate orderDate;
private ShippingDate shippingDate;
public Order(Quantity quantity, OrderDate orderDate, ShippingDate shippingDate){
//assign values without validation I think??!!
}
}
和类数量例如将是:
class Quantity {
private int quantity;
public Quantity(int quantity){
if(quantity <= 0){ throw new Exception("Invalid quantity")}
this.quantity=quantity;
}
}
现在问题:
Quantity
课程不是违反了吗?Quantity
的构造函数中的验证?我认为验证代码是重复的,所以如何验证它一次或至少重用验证逻辑。ShippingDate
依赖于OrderDate
进行验证,我应该如何验证发货日期?答案 0 :(得分:1)
Quantity
不能为负,无论上下文如何,这都是完全合理的Quantity
构造函数中的验证用于确保应用程序正确使用该类。它会引发异常,这些异常是针对特殊状态而不是预期的工作流程。因此,它与Web表单验证的目的完全不同,它确保正确使用您的应用程序。它期望无效输入并处理它。我在这里看不到真正的重复,至少在没有违反单一责任原则的情况下可以消除这种重复。if(shippingDate < orderDate)
- 您是如何计划在价值对象中对此进行验证的?Date
。答案 1 :(得分:0)
这些问题很多,您可能希望将它们分解为单个问题。
问题2到5在很大程度上取决于各种因素,并受到意见的影响。
但这是我对问题1的回答(有点问题3和4):
聚合负责其完整性。不是聚合根。只要Aggregate作为一个整体保持有效,Aggregate中的每个项目都可以进行自己的验证。
状态验证(作为正确的金额或不是负数的金额)可以在相应的类中完成。像ShippingDate >= OrderDate
这样的相互依赖状态验证可以在更高级别上完成,例如在聚合根中。
答案 2 :(得分:0)
聚合根应该在其聚合中强制执行不变量,但它们不会进行所有验证。特别是在施工时没有验证,通常在施工人员或工厂处理。
事实上,尽可能多地(非特定于上下文的)不变量移动到构造函数和工厂可能是有益的。我认为拥有always valid实体更好,而不是依赖于在聚合根或实体本身上重复使用ValidateThis()
和ValidateThat()
方法。
基本上有3种验证:客户端验证,应用程序验证(在控制器或应用程序层服务中)和域验证(域层)。客户端验证是必需的,不能重复使用。应用程序验证可以依赖于域验证,在您的示例中,这意味着只需调用Quantity构造函数并处理它引发的异常。但它也可以拥有自己的一组特定于应用程序的非域规则 - 例如,根据password
验证password_confirm
字段。
与总是有效的实体的精神相同,价值对象最好是不可变的,这意味着你只需要在新的时候验证一次。然而,这是内在验证,您可以在包含实体中完美地进行外围验证(例如,您的列表中不能有超过3个这样的值对象,值对象A始终与价值对象B 等相关。)
这是情境验证,而不是对ShippingDate内在不变量的验证。因此,Order应负责独立于每个值对象的有效性检查ShippingDate >= OrderDate
。
当对象的构造逻辑足够复杂以至于它本身就是一种责任时,应该使用工厂,因此,由于SRP,它不适合对象的构造函数或消费者。工厂确实包含构造时验证逻辑,就像构造函数一样,这使得它们也成为不变的执行者。