验证数据合法是DDD中富有争论的额一点,例如,货物达到最大重量的验证方式像这样:

    1. public class Cargo extends AggregateRoot {
    2. private int maxWeight;
    3. private List<Product> items;
    4. public Cargo(int maxWeight) {
    5. this.maxWeight = maxWeight;
    6. items = newList < Product > ();
    7. }
    8. public void addItem(Product product) {
    9. int currentWeight = items.stream().mapToInt(x -> x.weight).sum();
    10. if (currentWeight + product.weight > maxWeight)
    11. throw new InvalidStateException();
    12. items.add(product);
    13. }
    14. }

    在addItem方法中,如果产品重要超过最大重量将抛出异常且不会加入到list中。另一种方式是

    1. public class Cargo extends AggregateRoot {
    2. public int maxWeight;
    3. private List<Product> Items;
    4. public Cargo(int maxWeight) {
    5. maxWeight = maxWeight;
    6. items = newList < Product > ();
    7. }
    8. public void addItem(Product product) {
    9. items.add(product);
    10. }
    11. public boolean isValid() {
    12. int currentWeight = items.stream().mapToInt(x -> x.weight).sum();
    13. return currentWeight <= MaxWeight;
    14. }
    15. }

    这种方式允许任意重量的产品添加至列表。通过调用新增的isValid方法验证。这两种方式各有优劣:

    • 每步验证:直观
    • 最后验证:所有验证都位于同一位置

    每步验证的前提下,程序员很清楚验证细节;最后验证可以将验证代码聚合。如何选择?我的建议是使用每步验证:

    • 消除时间耦合
    • 遵循DRY原则

    原因有二,第一,最后验证这种方式需要在持久化前调用,且有时容易忘记;第二,每步验证遵循DRY原则,消除了代码重复。

    • 验证必须处于领域层
      1. String err = snackMachine.canBuySnack(position);
      2. if(err != string.isEmpty()){
      3. showError(error);
      4. return;
      5. }
      6. snackMachine.BuySnack(position);
      像这种在应用服务层的验证是不建议的。我们例子中的所有验证都是处在领域模型中的。