DDD, обработка зависимостей - PullRequest
7 голосов
/ 09 февраля 2010

Скучное вступление:

Я знаю - DDD не о технологиях. На мой взгляд, DDD - это создание повсеместно распространенного языка с владельцем продукта и его отражение в коде настолько простым и структурированным образом, что его просто нельзя неправильно истолковать или потерять.

Но тут вступает в игру парадокс - чтобы избавиться от технической стороны приложения в доменной модели, он становится своего рода техническим - по крайней мере, с точки зрения дизайна.

В прошлый раз, когда я пытался следовать DDD - все закончилось с целой логикой вне доменных объектов в «магические» сервисы вокруг и анемичную модель домена.

Я выучил несколько новых трюков ниндзя и подумал, смогу ли я справиться с Голиафом на этот раз.


Проблема:

class store : aggregateRoot { 
  products;
  addProduct(product){
    if (new FreshSpecification.IsSatisfiedBy(product))
      products.add(product);
  }
}

class product : entity {
  productType;
  date producedOn;
}

class productTypeValidityTerm : aggregateRoot {
  productType;
  days;
}

FreshSpecification предполагается указать, если продукт не пахнет. Чтобы сделать это - он должен проверить тип продукта, определить по дням, как долго продукт является свежим, и сравнить его с producedOn. Добрый простой.

Но тут возникает проблема - productTypeValidityTerm и productType должны управляться клиентом. Он должен иметь возможность свободно добавлять / изменять их. Поскольку я не могу перейти от продукта к productTypeValidityTerm напрямую, мне нужно как-то запросить их по productType.

Ранее - я хотел бы создать что-то вроде ProductService, которое получит необходимые репозитории через конструктор, запросит термины, выполнит некоторое дополнительное вуду и вернет логическое значение (убирает релевантную логику дальше от самого объекта и рассеивает его, кто знает, где).

Я думал, что было бы приемлемо сделать что-то вроде этого:

addProduct(product, productTypeValidityTermRepository){...}

Но опять же - я не мог свободно составить спецификацию из нескольких спецификаций, что является одним из их основных преимуществ.

Итак - вопрос в том, где это сделать? Как магазин может быть в курсе условий?

1 Ответ

2 голосов
/ 09 февраля 2010

С риском упрощения вещей: почему бы не сделать так, чтобы Product был свежим, что продукт «знает»? Store (или любой другой связанный объект) не должен знать, как определить, является ли продукт еще свежим; другими словами, тот факт, что что-то вроде freshSpecification или productTypeValidityTerm даже существует, не должен быть известен Store, он должен просто проверять Product.IsFresh (или, возможно, какое-то другое имя, которое лучше соответствует реальному миру, например ShouldbeSoldBy, ExpiresAfter и т. Д.). Затем продукт может знать, как на самом деле получить protductTypeValidityTerm путем внедрения зависимости хранилища.

Мне кажется, что вы экстернализуете поведение, которое должно быть присуще вашим агрегатам / сущностям вашего домена, в конечном итоге приводя (опять же) к анемичной модели домена.

Конечно, в более сложном сценарии, где свежесть зависит от контекста (например, то, что приемлемо в бюджетном магазине, не считается достойным продажи в магазине премиум-класса), вам нужно было бы экстернализировать все поведение, как от продукта и из хранилища, и вообще создайте другой тип для моделирования этого конкретного поведения.

Добавлено после комментария

Что-то в этом духе для простого сценария, который я упомянул: сделать FreshSpec частью агрегата Product, что позволяет ProductRepository (здесь вводится конструктор) загружать его при необходимости.

public class Product {
  public ProductType ProductType { get; set; }
  public DateTime ProducedOn { get; set; }
  private FreshSpecification FreshSpecification { get; set; }
  public Product(IProductRepository productRepository) { }

  public bool IsFresh() {
    return FreshSpecification
      .IsSatisfiedBy(ProductType, ProducedOn);
  }
}

Магазин не знает об этих внутренних деталях: все, что его волнует, является ли продукт свежим:

public class Store {
  private List<Product> Products = new List<Product>();
  public void AddProduct(Product product) {
    if (product.IsFresh()) {
      Products.Add(product);
    }
  }
}
...