Информация об изменениях

Сообщение Re[14]: DDD для небольших проектов. от 14.02.2020 16:52

Изменено 14.02.2020 16:56 Poopy Joe

Re[14]: DDD для небольших проектов.
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Poopy Joe, Вы писали:


PJ>>Тут ровно две степени сводобы оплачен или нет. Все остальные типы завсисимые.

S>Они были бы зависимыми, если бы мы писали на C++. Там бы мы параметризовали шаблонный тип Order всеми нужными нам аргументами, и безо всяких конструкторов писали бы прямо правила трансформации.
Они зависимые, даже если их описывать на ассемблере. Нельзя получить любой из них по своему выбору, можно только вывести один из другого, причем есть всего два пути.

S>Там да — можно было бы описать, что рефанд реквест возможен только для ордера, у которого проведён платёж, независимо от его остальных компонентов состояния.

S>А в дотнете вы выписываете все эти типы ордеров руками.
Вообще никакой разницы. Возможет только для PaidOrder

PJ>>Это все зависит от того как это системой обрабатывается. Можно при создании ShippableOrder создавать еще и PaymentRequest и обрабатывать его отдельно, позваляя, например, переводить деньги на счет. С собственно доставкой эта операция не больно-то связана. Не обязательно все валить в одну кучу.

S>Ну, с доставкой не связана. И как нам это поможет?
Это поможет не мешать мух с котлетами, например.

S>>>Надо полагать, будет что-то типа

S>>>
S>>>        public DeliveredOrder(ShippedPaidOrder order){} // тут paymentInfo и так есть
S>>>        public DeliveredOrder(ShippedUnpaidOrder order, PaymentInfo paymentInfo){} // а тут надо её предоставить
S>>>

S>>>Может, я чего-то не понимаю, но у нас только что класс ShippedOrder развалился надвое, чтобы была возможность сделать два разных конструктора для DeliveredOrder.
S>А заодно придётся развалить пополам и класс ShippableOrder.
PJ>>В каком, прости, месте? И в том, и в другом случае ты создаешь тип одын штука.
S>Как это в каком? Появились типы ShippedPaidOrder и ShippedUnpaidOrder. А как иначе мы выразим требование "заказ перед выдачей должен быть оплачен" при помощи типов?
А ты про них. Ну да, их стало два, ну в результате у тебя получился ровно один Delivered.



PJ>>Да хоть 110. Ты это говоришь с таким придыханим, как будь-то у тебя количество типов чем-то ограничено.

S>Отлично. Только что вы не верили, что у нас получится экспоненциальный рост количества классов. А теперь мы от фазы отрицания переходим к фазе гнева, да?
Может у нас разное понимание экспоненты? Ты это так любишь употреблять но порабы доказать уже математически.
Имеем,
                                                PaidOrder                       ShippedPaidOrder -------------------------
                                              /             \                /                                             NewOrder -> IncompleteOrder -> ValidatedOrder                 ShippableOrder                                                 Delivered  
                                              \ UnpaidOrder /                \  ShippedUnpaidOrder -> DeliveredWithPaiment /

Покажи мне тут экспоненту?
И, второй вопрос, как ты это обычно выражешь по-своему?

S>Как куда? В код бизнес-логики. Проверяем предусловия, и поехали. Пишется ровно такой же код "трансформации", только проверки предусловий выполняются прямо по месту действия правила, а не в момент конструирования аргумента.

А код он в C# не в типах живет? И как ты проверишь эту корретность когда поменяешь модель?

S>Ну вот я и не понимаю, как добиться того, "чего я хотел". Ну сделаю я этот Tuple — дальше-то что?

Ну откуда мне знать твою бизнес-задачу?! Я лишь заметил, что нет необходимости все валить в кучу, создавая God class с кучей методов.

PJ>>Не надо. C# поддерживает nullable reference types.

S>Вы хотели сказать "non-nullable reference types"? Ну, этой фиче без году неделя, у меня пока нет опыта пользования. В описании авторов это выглядит так, что "ну, мы вроде иногда чего-то там проверяем", поэтому уверенности в железобетонности "скомпилировалось — значит работает" у меня нету.
Фича называется nullable reference types, и, в описании авторов работает как заявлено. https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Я не понял к чему этот комментарий? Я должен был как-то понять, что ты в нее не веришь и написать проверку на null?
До этого можно было использовать [NotNull] атрибут от JetBrains.

S>Количество бойлерплейта радикально зависит от того, какой стиль кода выбран.

Да не особо. Тебе надо будет сделать все те же проверки, только в другом месте, и компилятор не будет тебе помогать поддерживать инвариант. Или ты под радикально другим стилем подразумеваешь спагетти-код, который вообще ничего не проверят и тупо валится при любом неверном параметре? Ну такой код будет короче, спору нет.

S> Пока что у меня впечатление, что только лишь F# имеет (если имеет) достаточно мощную систему типов, чтобы описывать бизнес-правила с нужной точностью.

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

S>Ок. А бизнес-логика, она в труъ DDD в агрегатах, или ещё где-то?

Задача агрегата поддерживать инвариант агрегата. Вот эта логика находится в нем. Бизнес-логика находится в другом слое, я выше описывал. Я выше ссылку на книжку давал, там это все подробно описано, советую найти и прочитать.
Re[14]: DDD для небольших проектов.
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Poopy Joe, Вы писали:


PJ>>Тут ровно две степени сводобы оплачен или нет. Все остальные типы завсисимые.

S>Они были бы зависимыми, если бы мы писали на C++. Там бы мы параметризовали шаблонный тип Order всеми нужными нам аргументами, и безо всяких конструкторов писали бы прямо правила трансформации.
Они зависимые, даже если их описывать на ассемблере. Нельзя получить любой из них по своему выбору, можно только вывести один из другого, причем есть всего два пути.

S>Там да — можно было бы описать, что рефанд реквест возможен только для ордера, у которого проведён платёж, независимо от его остальных компонентов состояния.

S>А в дотнете вы выписываете все эти типы ордеров руками.
Вообще никакой разницы. Возможет только для PaidOrder

PJ>>Это все зависит от того как это системой обрабатывается. Можно при создании ShippableOrder создавать еще и PaymentRequest и обрабатывать его отдельно, позваляя, например, переводить деньги на счет. С собственно доставкой эта операция не больно-то связана. Не обязательно все валить в одну кучу.

S>Ну, с доставкой не связана. И как нам это поможет?
Это поможет не мешать мух с котлетами, например.

S>>>Надо полагать, будет что-то типа

S>>>
S>>>        public DeliveredOrder(ShippedPaidOrder order){} // тут paymentInfo и так есть
S>>>        public DeliveredOrder(ShippedUnpaidOrder order, PaymentInfo paymentInfo){} // а тут надо её предоставить
S>>>

S>>>Может, я чего-то не понимаю, но у нас только что класс ShippedOrder развалился надвое, чтобы была возможность сделать два разных конструктора для DeliveredOrder.
S>А заодно придётся развалить пополам и класс ShippableOrder.
PJ>>В каком, прости, месте? И в том, и в другом случае ты создаешь тип одын штука.
S>Как это в каком? Появились типы ShippedPaidOrder и ShippedUnpaidOrder. А как иначе мы выразим требование "заказ перед выдачей должен быть оплачен" при помощи типов?
А ты про них. Ну да, их стало два, ну в результате у тебя получился ровно один Delivered.



PJ>>Да хоть 110. Ты это говоришь с таким придыханим, как будь-то у тебя количество типов чем-то ограничено.

S>Отлично. Только что вы не верили, что у нас получится экспоненциальный рост количества классов. А теперь мы от фазы отрицания переходим к фазе гнева, да?
Может у нас разное понимание экспоненты? Ты это так любишь употреблять но порабы доказать уже математически.
Имеем,
                                                PaidOrder                       ShippedPaidOrder -------------------------
                                              /             \                /                                             \
NewOrder -> IncompleteOrder -> ValidatedOrder                 ShippableOrder                                                 Delivered  
                                              \ UnpaidOrder /                \  ShippedUnpaidOrder -> DeliveredWithPaiment /

Покажи мне тут экспоненту?
И, второй вопрос, как ты это обычно выражешь по-своему?

S>Как куда? В код бизнес-логики. Проверяем предусловия, и поехали. Пишется ровно такой же код "трансформации", только проверки предусловий выполняются прямо по месту действия правила, а не в момент конструирования аргумента.

А код он в C# не в типах живет? И как ты проверишь эту корретность когда поменяешь модель?

S>Ну вот я и не понимаю, как добиться того, "чего я хотел". Ну сделаю я этот Tuple — дальше-то что?

Ну откуда мне знать твою бизнес-задачу?! Я лишь заметил, что нет необходимости все валить в кучу, создавая God class с кучей методов.

PJ>>Не надо. C# поддерживает nullable reference types.

S>Вы хотели сказать "non-nullable reference types"? Ну, этой фиче без году неделя, у меня пока нет опыта пользования. В описании авторов это выглядит так, что "ну, мы вроде иногда чего-то там проверяем", поэтому уверенности в железобетонности "скомпилировалось — значит работает" у меня нету.
Фича называется nullable reference types, и, в описании авторов, работает как заявлено. https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Я не понял к чему этот комментарий? Я должен был как-то понять, что ты в нее не веришь и написать проверку на null?
До этого можно было использовать [NotNull] атрибут от JetBrains.

S>Количество бойлерплейта радикально зависит от того, какой стиль кода выбран.

Да не особо. Тебе надо будет сделать все те же проверки, только в другом месте, и компилятор не будет тебе помогать поддерживать инвариант. Или ты под радикально другим стилем подразумеваешь спагетти-код, который вообще ничего не проверят и тупо валится при любом неверном параметре? Ну такой код будет короче, спору нет.

S> Пока что у меня впечатление, что только лишь F# имеет (если имеет) достаточно мощную систему типов, чтобы описывать бизнес-правила с нужной точностью.

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

S>Ок. А бизнес-логика, она в труъ DDD в агрегатах, или ещё где-то?

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