5

我在伪代码中有一个简单的实体: 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验证和实体验证之间是重复的。我试图通过创建类、类和类
来将值对象的概念引入我的实体。所以我的实体变成了这样:QuantityOrderDateShippingDateOrder

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??!!
    }
}

例如,类 Quantity 将是:

类数量{

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

}

现在的问题:

  1. 聚合根不应该是负责验证整个聚合的吗?我的Quantity班级不是违反了吗?
  2. 如何Quantity在 Web 表单验证的构造函数中重用验证?我认为验证代码是重复的,所以我怎样才能验证一次或至少重用验证逻辑。
  3. 由于所有值对象都将验证自己,这是否意味着我不应该验证实体中的任何内容?
  4. 由于ShippingDate依赖于OrderDate验证,我应该如何验证发货日期?
  5. DDD 工厂适合所有这些吗?
4

3 回答 3

2
  1. 如果Quantity在您的领域中任何不能是负面的,无论上下文如何,这都是非常有意义的
  2. Quantity恕我直言,您的构造函数中的验证用于确保应用程序正确使用该类。它会引发异常,这些异常用于异常状态,而不是预期的工作流。因此,它具有与 Web 表单验证完全不同的目的,它确保用户正确使用您的应用程序。它期望无效输入并处理它。我在这里看不到真正的重复,至少不会在不违反单一职责原则的情况下消除这种重复。
  3. 我认为情况并非如此。你有if(shippingDate < orderDate)- 你打算如何在值对象中验证这一点?
  4. 啊,你看到问题了。相同的答案:此验证属于 Order 实体。此外,您不必对所有内容都使用值对象。如果订单日期或发货日期本身没有固有限制,请继续使用Date.
  5. 这似乎是一个单独的问题,我认为与价值对象没有任何相关性。
于 2013-01-22T12:49:35.133 回答
2
  1. 聚合根应该在它们的聚合中强制执行不变量,但它们并不进行所有验证。尤其是在构造时的验证,这通常在构造函数或工厂中处理。事实上,将尽可能多的(非上下文特定的)不变量移动到构造函数和工厂可能是有益的。我认为始终拥有有效的实体而不是依赖于聚合根或实体本身上重复使用ValidateThis()和方法是一个更好的主意。ValidateThat()

  2. 基本上有 3 种验证:客户端验证、应用程序验证(在控制器或应用程序层服务中)和域验证(域层)。需要客户端验证,不能重复使用。应用程序验证可以依赖于域验证,在您的示例中,这意味着只需调用 Quantity 构造函数并处理它引发的异常。但它也可以拥有自己的一组特定于应用程序的非域规则 - 例如,password根据其password_confirm.

  3. 与始终有效的实体大致相同,值对象最好是不可变的,这意味着您只需在新建它们时验证一次。但是这是内在验证,您可以在包含实体中完美地进行外围验证(例如,您的 list 中不能有超过 3 个此类值对象,值对象 A 总是与值对象 B 一起使用,等等)

  4. 这是情景验证,而不是 ShippingDate-intrinsic 不变量的验证。因此,Order 应该负责ShippingDate >= OrderDate独立于每个值对象的有效性进行检查。

  5. 当对象的构造逻辑足够复杂以至于它本身就是一种责任,并且由于 SRP 不适合对象的构造函数或消费者时,应该使用工厂。工厂确实包含构造时验证逻辑,就像构造函数一样,这使得它们也成为各种不变的强制执行者。

于 2013-01-23T13:35:34.007 回答
0

这些问题很多,您可能希望将它们分解为单个问题。

问题 2 到 5 在很大程度上取决于各种因素,并受制于意见。

但这是我对问题 1 的回答(以及对问题 3 和 4 的回答):

聚合对其完整性负责。不是聚合根。只要聚合作为一个整体保持有效,聚合内的每个项目都可以进行自己的验证。

状态验证(作为正确的数量或不是负数的数量)可以在相应的类内完成。相互依赖的状态验证ShippingDate >= OrderDate可以在更高级别上完成,例如在聚合根中。

于 2013-01-22T12:51:05.453 回答