IdiocyAcceptance
Мне кажется лучше чётко сформулировать свою точку зрения о "making illegal state unrepresentable" через призму F#, а то ответов, если честно, все эти обвинения не дают)
Ну это в целом к вопросу о "making illegal state unrepresentable". Я вот пытался экспериментировать, но пока сложно найти хорошее решение при обработке ошибок
Vagif
Не то чтобы я очень активно это применяю, но вижу в этом резон.
IdiocyAcceptance
Особенно когда таких типов больше, чем 1.
Shub
В какой второй статье?
Effective ML revisited - это вторая статья по теме. Первая - это выступление в Корнуэлле, предназначенная для студентов, а потому полная капитанства для людей с «от 5 лет стажа»
Ayrat
Разве в таком случае мало убрать option и nullable? А что насчёт PositiveInt, String50 и прочих решений?
Это тоже недостаточно репрезентативно. Позитив инт - абстрактно как и uint. Надо - EmployeeSalary String50 туда же. Надо Email
IdiocyAcceptance
Это тоже недостаточно репрезентативно. Позитив инт - абстрактно как и uint. Надо - EmployeeSalary String50 туда же. Надо Email
Ну в целом да, вопрос для меня лично пока - как работать с ошибками всего этого зоопарка красивых и защищённых типов?
Vagif
Ну это в целом к вопросу о "making illegal state unrepresentable". Я вот пытался экспериментировать, но пока сложно найти хорошее решение при обработке ошибок
Проблема с String50 в том, что в спецификацию типа вытащено черт знает что не из предметной области. С Email вместо этого проблем нет
Shub
Я просто вижу примеры конкретного дизайна, когда разработчики, ссылаясь на Мински (а многие приписывают эту цитату Влашину), убирают из своих типов option и nullable, делая модель более строгой и жесткой. Не совсем понимаю, что в этом может раздражать.
То, что они понятия не имеют, какой эта модель должна быть, но уже начитают цементировать свое ограниченное понимание. А потом тупо сваливают на другой проект, потому что не могут мейнтейнить свои окаменевшие погадки
IdiocyAcceptance
Проблема с String50 в том, что в спецификацию типа вытащено черт знает что не из предметной области. С Email вместо этого проблем нет
Да, согласен что String50 не несёт доменной нагрузки и вероятно ценность таких типов тем выше, чем они сложнее или важнее для домена
Vagif
То, что они понятия не имеют, какой эта модель должна быть, но уже начитают цементировать свое ограниченное понимание. А потом тупо сваливают на другой проект, потому что не могут мейнтейнить свои окаменевшие погадки
Давай все же разделим вопросы собственно дизайна и управления коллективами. Плюс я не совсем понимаю, как в пятницу, накануне выходных, можно быть таким агрессивным 😊
Shub
Ну в принципе да
Но ведь этого никогда не произойдёт. Потому что по некоторому размышлению станет понятно, что этот тип нормально реализуется только как класс. А класс - это харам, Влашин накажет. Поэтому String50, тем более, что уже есть блог с реализацией, «все готово»
Shub
Дело не в управлении коллективом, это чисто дизайн
Vagif
Не надо быть настолько defensive, мы тут все свои и просто делимся мнениями
Но сложно порой понять, что ты имеешь в виду, когда все таким количеством гнева обмазано. Я не подкалываю, на самом деле не понимаю, почему тебя все это так раздражает
IdiocyAcceptance
Но ведь этого никогда не произойдёт. Потому что по некоторому размышлению станет понятно, что этот тип нормально реализуется только как класс. А класс - это харам, Влашин накажет. Поэтому String50, тем более, что уже есть блог с реализацией, «все готово»
Ну, "всё готово" к сожалению начинает ломаться о реальные нужды. Вот у тебя функция, которая принимает string50 и email666. Для её вызова уже нужны доп. обвязки. Как их реализовать с учётом того, что при валидации типов у них могут быть разные ошибки (разных типов в первую очередь) уже вопрос.
IdiocyAcceptance
Особенно если оглядываться не на исключения, а на некоторый "правильный" ROP-подход
Shub
В прикладной продуктовой разработке не бывает финальных спецификаций. Любая спецификация будет изменяться вплоть до полностью противоположных требований. Поэтому любые попытки «зафиксировать инварианты» на любой из стадий сродни направлению заряженного солью дробовика себе в пах
Shub
Иногда как класс, иногда не как класс. И выбор класса или некласса - это уже implementation details
Почти всегда как класс. Потому что в F# нет модулей и классы - это единственный инструмент для инкапсуляции. До Влашина это не дошло на момент написания книги, поэтому код, написанный его последователями обязательно выставляет свои кишки наружу
Shub
В 99% кодерок пытается зафиксировать вообще не то и не так, прикрываясь какой-то там «корректностью»
Shub
Корректностью чего, спрашивается? Утром приходит ПМ и говорит «помните, мы запрещали пользователям делать Х? Короче, теперь можно и нужно делать Х» – ну и где теперь ваша корректность?
Anonymous
В 99% кодерок пытается зафиксировать вообще не то и не так, прикрываясь какой-то там «корректностью»
А на каком-нить примере из практик можно? Типа, пытались фиксировать вот это, а надо было вот то и поэтому эти говноделы свалили с проекта через полгода, оставив да собой мертвечину в виде $proglang кода?
Anonymous
А то не очень понятно как надо.
Doge
В 99% кодерок пытается зафиксировать вообще не то и не так, прикрываясь какой-то там «корректностью»
Ну есть всё же инварианты подобного плана, вероятность нарушения которых оущтимо меньше. Например, newtype'ы для айдишников того или иного вида
Shub
Ну есть всё же инварианты подобного плана, вероятность нарушения которых оущтимо меньше. Например, newtype'ы для айдишников того или иного вида
любителям подергать айдишники вручную, а так же любителям однобуквенных переменных это безусловно важно. у остальных же за всю карьеру ошибки такого типа если и возникали, то в стастически незначительных количествах. но у большинства не возникали, т.к. цивилизация подарила массу способов вообще не встревать в такую ситуацию
Ilya
А потом придёт тг и скажет, что теперь id не помещаются в int32, надо 52 бита!
Shub
короче есть автоматическая система доставания предметов из коробок. огромная такая штука три этажа высотой.
Shub
общение с ней происходит через обмен сообщениями, сообщения представляют условно "команда-ответ": мы просим эту систему что-то сделать, система подтверждает согласие и репортит статусы обратно
Shub
условно назовем модель на нашей стороне "заданием". т.к. процесс небыстрый (физические предметы в физическом мире перемещаются как материальная точка по законам ньютоновской механики), пользователи хотят отслеживать состояние "задания"
Shub
ну вот год все работало по приблизительно такой логике: "задание отправлено" - "задание принято" - "предмет №1 достали" - "предмет №2 достали" - "задание закончено". альтернативное развитие событий - "задание отправлено" - "в задании отказано"
AlexB
ДДД в терминальной стадии?
В чём тут терминальность? Один тип натягивать на разные сущности - вот это действительно типичная "терминальность"
Shub
ну вот кодерок решил, что обладает всей полнотой знаний о возможных ситуациях и закодил модель "задание" как DU
Shub
более того, он там потратил кучу времени, чтобы не дай бог "задача" в неправильном состоянии не попала в ту или иную веточку. причем сделал он это довольно странно: все равно матчил и кидал исключение
Ilya
В чём тут терминальность? Один тип натягивать на разные сущности - вот это действительно типичная "терминальность"
критерий разные сущности может быть смещён в сторону обжектов или в сторону кастомных типов для всего
Shub
ну и случилась такая штука: выяснилось, что люди на складе тратят по нескольку часов вручную откатывая задания, которые посланы в эту штуку за 15 минут до отъезда последнего курьера - потому что процесс занимает минимум полчаса
Shub
причем сама эта штука автоматическая была в курсе этого всего и если там не удавалось достать какой-то предмет в течении заданного времени - задача отменялась с рассылкой уведомления во все заинтересованные в этом системы. в том числе и в нашу
Shub
ну мы по ходу просто падали с исключением, что банально рестартовало сервис. но я щас не об этом
Shub
ПМы посовещались и решили, что раз все вокруг могут корректно обрабатывать такую ситуацию - то и нам пожалуй тоже надо, не так ли?
Shub
и вот внесение этого банального изменения вылилось в 3 недели работы. потому что теперь надо перелопатить весь этот безумный ФСМ и выпилить все эти проверки на "некорректное состояние". потому что оно внезапно стало корректное
Shub
и таких примеров у меня приблизительно все реквесты от продукта за последние 3 месяца.
Ilya
Ну вот, так я уже делаю.
Shub
все правильно. айди сущности не имеет никакого отношения к логике продукта. поэтому оно должно быть скрыто от манипуляций и появляться только где-то там далеко в слое, отвественном за загрузку и сохранение.
Doge
не передавать айди. передавать сущности
Тут вопрос в том, что это не всегда возможно и не везде
Shub
Тут вопрос в том, что это не всегда возможно и не везде
да. но при этом ситуаций, когда у тебя в руках более двух айдишников, строго говоря быть не должно.
Shub
и если это как-то обусловлено независимыми от тебя обстоятельствами, время существования голых айдишников должно быть минимальным
Doge
да. но при этом ситуаций, когда у тебя в руках более двух айдишников, строго говоря быть не должно.
То есть у нас на прошлой работе достаточно много было работы с АПИ, которые работают строго с айдишниками и с сущностями о которых мы ничего не знаем, кроме Id.
Doge
И условно это самое MyEntityId и становится вырожденным обьектом сущности
Shub
То есть у нас на прошлой работе достаточно много было работы с АПИ, которые работают строго с айдишниками и с сущностями о которых мы ничего не знаем, кроме Id.
это не значит, что вы работаете с айдишниками. это значит, что у вас такие вырожденные модели из одного поля
Shub
что нормально. но это никак не означает, что для каждого айдишника надо заводить отдельный тип
Shub
и уж тем более это никак не оправдывает дел типа type MyCustomId = string
Doge
и уж тем более это никак не оправдывает дел типа type MyCustomId = string
Тут вопрос скорее как быть с ситуациями, когда выставляются АПИ, работающие именно с Id. Делать вырожденную DTO под эти случаи?
Shub
а, ну да.
Anonymous
и вот внесение этого банального изменения вылилось в 3 недели работы. потому что теперь надо перелопатить весь этот безумный ФСМ и выпилить все эти проверки на "некорректное состояние". потому что оно внезапно стало корректное
а, ты про это. я думал, ты все же высказывался про некий орхи-тектурный уровень понимания, когда инварианты системы фиксируются на уровне принципиальных сущеностей домена и типов взаимосвязей между ними.
Anonymous
а это история про чувака, который влез в проблему, не понимая всех частных случаев.
Anonymous
например, про любую тразнакцию можно с уверенность говорить, что она должна быть либо закомичена, либо заролбечена, но не то и другое вместе. это честный инвариант на уровне системы в целом. я про нечто такое.
Shub
поэтому для многих его совет звучит как "цементируйте ваше понимание немедленнно по получению первого задания"
Anonymous
я уже тогда поржал
Shub
Тут вопрос скорее как быть с ситуациями, когда выставляются АПИ, работающие именно с Id. Делать вырожденную DTO под эти случаи?
у вас нет другого выхода, особенно если это мейнстимный язык какой-то. в F# есть библиотечка, но я юз кейсов не встречал. могу поверить, что они есть, конечно
Doge
ну если у вас их там сотни разных в одной АПИ, то type EntityId 't = EntityId of 't?
Ну вот примерно об этом и речь. Ньютайпы, чтобы именно уровень АПИ типизировать чуть лучше обычного.
Doge
типа /api/v1/users/<id:long>?
Ну только много разных типов id
Anonymous
Ну только много разных типов id
когда юзер 1 к 1 с long и uuid например? типа, на сущность два уникальных идентификатора навешано?
Doge
когда юзер 1 к 1 с long и uuid например? типа, на сущность два уникальных идентификатора навешано?
Нет, когда есть действие в духе подвердить данный набор товаров (по id) в данном заказе (по id) по данной квоте (по id). (Пример из головы, если что) И т.п.
Shub
ага, я это еще со времен Verified Email | UnverifiedEmail помню
об этом и речь. Мински приводит примеры моделей, которые в общем-то хорошо известны - биржевый ордер там, и т.п., вещи, устоявшиеся на протяжении десятков лет
Shub
Нет, когда есть действие в духе подвердить данный набор товаров (по id) в данном заказе (по id) по данной квоте (по id). (Пример из головы, если что) И т.п.
а разве тебе не надо загрузить все эти сущности? трудно представить, как можно построить логику на этом всем на одних только айдишниках?
Doge
так а в чем именно там особсенность? я не очень уловил где сложность
Идея в том, чтобы предположем метод контроллера не выглядил как ConfirmationResult Confirm(long orderId, long quotaId, long[] wareIds) А выглядил как: ConfirmationResult Confirm(OrderId orderId, QuotaId quotaId, WareId[] wareIds)