Re[17]: DDD для небольших проектов.
От: Sinclair Россия https://github.com/evilguest/
Дата: 21.02.20 07:23
Оценка:
Здравствуйте, Poopy Joe, Вы писали:

PJ>Здравствуйте, Sinclair, Вы писали:


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

S>>Это понятно. Просто на ассемблере придётся писать руками вообще всё, на шарпе — чуть меньше, а F#, как я понимаю, обходится без бойлерплейта.
PJ>Не в этом дело. Они зависимые потому, что не всякий тип создает ветвление. Если есть трансформации a -> b -> c -> d, то путь ровно один, хотя типов 4 штуки.

PJ>>>Вообще никакой разницы. Возможет только для PaidOrder

S>>Я не понимаю что такое "вообще никакой разницы". Вы только что мне привели в пример простыню кода, которая всё ещё ничего не делает, кроме как описывает статические бизнес-правила.
S>>Не умаляю полезности этого, но по соотношению "логика на строчку кода" этот пока не выигрывает у любого другого варианта.

PJ>Никакой разницы потому, что функция refund принимает только параметр PaidOrder, что логично не так ли?

PJ>Если валидность PaidOrder гарантирована, то это все что надо знать refund. Оно по-определению, в этом случае, не зависит от остальных компонентов системы.
PJ>Что, в конечном итоге, уменьшает размер кода и делает его более надежным.
PJ>Если есть валидные a, b и c, и корректные a -> b, b -> c, то функция a -> b -> c будет тоже корректна. А компилятор не даст тебе собрать d -> c.

S>>У меня и до этого был один Delivered.

PJ>Ну т.е. типы не расползаются. Начал с одного и закончил одним. Какая же это экспонента?
Кроме Delivered у нас ещё много промежуточных типов.

PJ>Что значит склеил? ShippableOrder их ко-произведение. Я использую логику процесса, как я его понимаю. На мой взгляд готовность к отправке не зависит от статуса оплаты, по твоим объяснениям.

Да, не зависит. Но мне непонятно, как вы собираетесь получать из ShippableOrder при помощи одной операции два разных типа — UnpaidShippedOrder и PaidShippedOrder.

PJ>А зачем бы мне это делать? Paid/Unpaid содержит разную информацию, поэтому типы разные. И дает две дополнительные функции refund : PaidOrder -> Result<> и requestPayment : PaidOrder -> Result<>

PJ>Зачем нужен второй prepareToShip я не представляю, но вполне допускаю, что смысл в этом может быть. Но, в этом случае, хоть с типами, хоть без, у тебя ровно два решения: две функции или две ветки if-else. А как еще?
Ну, очень просто. В плюсах это была бы частичная специализация; в C#/Java у нас была бы простая проверка предусловия. Причём, возможно, вообще не выраженного в коде приложения — была бы конфигурация предиката, которую может править администратор вообще без единого запуска компилятора. Кто ж может себе позволить ре-компиляцию и ре-деплоймент каждый раз, как надо изменить одно из правил

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

S>>Обычно это выражается ровно в тех правилах, которые описывают переход состояний. В том методе, который пытается изменить статус отгрузки, проверяется наличие пре-реквизитов отгрузки. И ему всё равно, сколько ещё есть признаков и какие там у них значения. В терминах типов, этот метод полиморфен — он принимает и PaidShippableOrder, и UnpaidShippableOrder, и любой другой ShippableOrder.
PJ>Ты сам говорил про важность кода. Вот тут как раз такой случай.
public static void ShipOrder(Order o)
{
   Assert(ShippingManager.IsShippingAddressValid(o.ShipmentMethod, o.ShippingAddress);
   Assert(BillingManager.IsOrderOkForDelivery(o.BillingMethod, o.Customer);
   Assert(o.ProcessingStage == ProcessingStage.ReadyForPickup);
   var pickupSchedule = ShippingManager.SchedulePickup(o);
   OrderManager.SetStage(ProcessingStage.PickupScheduled, pickupSchedule);  
}


PJ>Реально заначимый код это sunny day path. Все остальное это бойлерплейт. Удачи с таким кодом.

По моему опыту, sunny day path — это 7% кода. Как раз он и есть boilerplate. Всё остальное — это детальные политики того, что делать, когда всё пойдёт не так.
Фрод скрининг, пересортица, обработка отказов сети, обработка отказов от партнёров, вот это вот всё. Потому что happy path — в основном один, или два, а способов сломаться — много.

и C# всё отлично откомпилирует, а F# бы дал по рукам?
PJ>Смысл моей фразы в том, что все описанное на f# можно сделать и на c#. Я вовсе не призываю тебя писать на c#, если есть возможность писать на f#, это было бы слабоумно. Но такая возможность не всегда доступна.
А этот смысл — он чем-то другой, чем "все тьюринг-полные языки функционально эквивалентны"? Ну, т.е. следует ли из того, что всё, описанное на F#, можно сделать и на IL, оправданность применения IL для описания вот этих вот систем зависимых типов?

PJ>Ну вот мы, например, заморочились и используем IO на f#. Разумеется чистоту функций компилятор не проверяет, приходится делать через код-ревью. Тайп-классов тоже нет.

PJ>Больше писать? Да, больше. Меньше компилятор помогает? Да, меньше.
PJ>Тем не менее возможность вынести сайд-эффекты на границы домена многократно повышеат читаемость, тестируемость и, в конечном итоге, надежность.
PJ>Дебажить практически не надо, а тех редких случаях когда приходится, ты точно знаешь место где это надо посмотреть.
PJ>То же самое и в C# или любом другом языке. Либо ты больше используешь компилятор, либо дебаггер. Как-то так...
Не вполне понимаю всё же как именно выносить сайд-эффекты на "границы домена".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.