Скучное введение:
Я знаю - DDD не о технологии. Поскольку я вижу его - DDD - все о создании повсеместного языка с владельцем продукта и отражением его в код таким простым и структурированным способом, что это просто не может быть неправильно истолковано или потеряно.
Но здесь прибывает парадокс в игру - для избавлений от технической стороны приложения в модели предметной области, это становится добрым техническое - по крайней мере, с точки зрения дизайна.
В прошлый раз я пытался следовать за DDD - он закончился с целой логикой за пределами объектов области на 'волшебные' службы все вокруг и анемичная модель предметной области.
Я изучил некоторые новые приемы ниндзя и задающийся вопросом, мог ли я обработать Goliath на этот раз.
Проблема:
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){...}
Но с другой стороны - я не мог составить спецификацию из нескольких спецификаций внизу свободно, что является одним из их основных преимуществ.
Таким образом - вопрос, где сделать это? Как хранилище может знать об условиях?
С риском чрезмерного упрощения: почему бы не сделать тот факт, что Продукт
свежий, чем-то «знает» продукт? Магазин
(или любой другой связанный объект) не должен знать, как определить, свежий ли продукт; другими словами, тот факт, что что-то вроде 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);
}
}
}