在进行领域驱动设计时如何正确利用值对象验证?

Son*_*ngo 6 language-agnostic oop domain-driven-design

我有一个伪代码中的简单实体 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
    }
}
Run Code Online (Sandbox Code Playgroud)

描述、数量、订单日期和发货日期都是从 Web 表单中读取的,其中每个都是由多个验证器配置的文本字段:

quantityField= new TextField('txt_quantity');
quantityFiled.addNotNullValidator().addNumaricValidator().addPositiveIntegerValidator()
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,验证逻辑在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??!!
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,类数量将是:

类数量{

private int quantity;
public Quantity(int quantity){
        if(quantity <= 0){ throw new Exception("Invalid quantity")}
        this.quantity=quantity;
}
Run Code Online (Sandbox Code Playgroud)

}

现在的问题是:

  1. 聚合根不应该负责验证整个聚合吗?Quantity我的班级是不是违反了这一点?
  2. 如何在QuantityWeb 表单验证的构造函数中重用验证?我认为验证代码是重复的,所以我如何验证它一次或至少重用验证逻辑。
  3. 由于所有值对象都会验证自身,这是否意味着我不应该验证实体中的任何内容?
  4. 由于ShippingDate取决于OrderDate验证,我应该如何验证发货日期?
  5. DDD 工厂在这一切中扮演什么角色?

Fab*_*ler 3

  1. 如果任何Quantity在您的领域中不能为负数,无论上下文如何,这都是完全有道理的
  2. 恕我直言,构造函数中的验证Quantity用于确保应用程序正确使用该类。它抛出异常,这些异常是针对异常状态,而不是针对预期的工作流程。因此,它的用途与 Web 表单验证完全不同,Web 表单验证可确保用户正确使用您的应用程序。它期望无效输入并处理它。我在这里没有看到真正的重复,至少没有看到可以在不违反单一责任原则的情况下消除的重复。
  3. 我认为情况并非如此。您if(shippingDate < orderDate)计划如何在值对象中验证这一点?
  4. 啊,你看到问题了。相同的答案:此验证属于 Order 实体。此外,您不必对所有内容都使用值对象。如果订单日期或发货日期各自没有固有的限制,则继续使用Date
  5. 这似乎是一个单独的问题,我没有看到与值对象有任何相关性。