Здравствуйте, Sinclair, Вы писали:
PJ>>Какая разница? Это BL.
S>Нууу, как же — большая разница.
Очень информативно.
S>Ну, вот у нас уже получилось 8 типов заказа — ровно 2-в-степени-количество-степеней-свободы.
Тут ровно две степени сводобы оплачен или нет. Все остальные типы завсисимые.
S>И эта модель у нас теряет информацию: после отправки у нас уже нет никаких данных о том, был ли заказ оплачен. Ну, то есть наверное можно попробовать это починить, добавив в ShippedOrder и DeliveredOrder свойство Order по образцу ShippableOrder. Но не факт, что этого достаточно: если мы хотим потребовать платёж-при-получении от наших Shippable(UnpaidOrder), то как будет выглядеть сигнатура конструктора DeliveredOrder?
Это все зависит от того как это системой обрабатывается. Можно при создании ShippableOrder создавать еще и PaymentRequest и обрабатывать его отдельно, позваляя, например, переводить деньги на счет. С собственно доставкой эта операция не больно-то связана. Не обязательно все валить в одну кучу.
S>Надо полагать, будет что-то типа
S>S> public DeliveredOrder(ShippedPaidOrder order){} // тут paymentInfo и так есть
S> public DeliveredOrder(ShippedUnpaidOrder order, PaymentInfo paymentInfo){} // а тут надо её предоставить
S>
S>Может, я чего-то не понимаю, но у нас только что класс ShippedOrder развалился надвое. А заодно придётся развалить пополам и класс ShippableOrder.
В каком, прости, месте? И в том, и в другом случае ты создаешь тип одын штука.
S>Итого — уже 10 классов. И это мы ещё не начали думать о возвратах. Либо мы переносим данные из статического типа в динамическое состояние — как вы поступили с ShippableOrder.
Да хоть 110. Ты это говоришь с таким придыханим, как будь-то у тебя количество типов чем-то ограничено. А куда ты поместишь код когда начнешь думать о возвратах? В астрал?
S>Теперь, чтобы узнать, нуждается ли ShippableOrder в оплате, придётся делать рантайм-анализ:
S>S>var paymentInfo = (shippableOrder.Order is PaidOrder paidOrder) ? paidOrder.PaymentInfo : UI.RequestPaymentInfo();
S>
Детали реализации. Может да, может нет. Никто не мешает имееть структуру Tuple<ShippedOrder, PaymentRequest> Все эти типы можно как угодно массировать до получения полного удовлетворения и уверенност, что вот сейчас все так как ты и хотел.
PJ>>А я показал, что это не так.
S>Пока что вы подтверждаете мои опасения.
Разве что конструкторы считать "разваливанием класса на два"
S>Ну как же. Вот у вас почти у всех ордеров есть конструкторы. Вы их так пишете, как будто достаточно перечислить аргументы, и всё заработает. Ну дак ведь нет — надо же весь этот бойлерплейт писать руками:
S>S>public ValidOrder(NotEmptyList<Article> articles, Address address, Customer customer, StockReference reference)
S>{
S> Articles = articles ?? throw new ArgumentNullException(nameof(articles));
S> Address = address ?? throw new ArgumentNullException(nameof(address));
S> Customer = customer ?? throw new ArgumentNullException(nameof(customer));
S> Reserved = reference ?? throw new ArgumentNullException(nameof(reference));
S>}
S>
Не надо. C# поддерживает nullable reference types.
Если ты хочешь наехать на бойлерплейт в C#, то есть куда более болезенные места. Например, дефолтный equality comparer, который проверяет ссылку, отсутствие ADT, нормальной композии и убогий паттерн-матчинг.
Но так все это одинаково справедливо хоть с DDD, хоть без. C# это путь боли. Но это ж добровольный выбор.
S>При этом компилятор всё ещё ни за чем из этого не следит — я могу запросто забыть проинициализировать свойство в конструкторе, и оно прекрасно останется null. В итоге код компилируется, хотя и падает при первом же юнит тесте.
Следит. Хотя пока в виде варнинга, но варнинги можно и как ошибки поставить.
PJ>>Так мне тоже, но вот ты, не стесняясь, требуешь примеров кода, хотя на них времени надо больше, чем на просто текст.
S>К сожалению, без кода вести технические дискуссии бессмысленно.
Уж точно бессмысленно требовать от других, не показывая свой. Гораздо больше хочется помочь человеку, который действительно прилагает усилия понять и что-то сделать.
PJ>>Это никакого отношения к DDD не имеет. Никто не запрещает далать любые оптимизации. DDD запрещает делать их из модели, поскольку они относятся к деталям бд, от которой не зависит бизнес-логика.
S>Ну ок. Модель — это, вроде бы, entities. Агрегаты в вашей версии DDD есть? Или нету?
Эээ...?! Модель это модель. В DDD есть value objects, то у чего нет ID. Адрес например. Entities — то, у чего есть ID. Иначе говоря разные инстансы могут ссылаться на одну entity. И агрегаты — коллекции entities. В данном пример customer, article это, очевидно, ссылки на них. Так что order это агрегат и есть.