Aleksandr
Никто так и не объяснил почему
Vladislav
Но чем лучше собирать все руками вместо тупой регистрации мне не понятно
Vladislav
Кроме мнимой типобезопасности
Aleksandr
Не такой уж и мнимой. Уж сколько раз ломал и чинил приложения по причине: незареген сервис
Roman
Никто так и не объяснил почему
классический сервис локатор работает так: у тебя есть большая свалка, в которую динамично можно добавлять и оттуда доставать сервисы. И ты вместо конкретных зависимостей прокидываешь этот мешок, и на лету в каком-нить методе сервиса оттуда достаешь ту зависимость, которую надо. Проблема этого подхода в одновременной динамичности и неявности. Неявность в том, что неочевидно, где какие зависимости нужны, и какие вообще следует регистрировать. Динамичность — пушто и регистрация и резолвинг происходит в рантайме. В итоге у тебя все компилится, куча тестов работает, но иногда в рантайме совершенно неожиданно отъебывает, пушто вдруг какого-то сервиса не нашлось. И от этого надежной защиты в сервис локаторе нет
Roman
Но чем лучше собирать все руками вместо тупой регистрации мне не понятно
тем, что тут заранее явно и очевидно, что и где надо зарегать. И ничего в рантайме не отъебнет
Vladislav
Хз, переизобретение колёса и не более
Roman
А так, тебе все равно плюс минус руками композишн рут собирать. С динамическим контейнером тебе немного проще написать код собирания зависимостей, но существенно сложнее отслеживать, что никто не забыт ничто не забыто
Aleksandr
Vladislav
ок как скажешь
Ну ты сделал портянку интерфейсов с аггрегатом в виде обычного ди
Roman
Аргумент про динамичность работает так же и для DI с пресловутым constructor injection. Про неявность - согласен
да, конструктор инжекшн так же динамичен, но уже хотя бы очевиден список зависимостий
Vladislav
Посмотрим когда зависимостей много
Vladislav
Что будет
Roman
Constructor injection можно проверить на стадии компиляции
как ты проверишь на стадии компиляции, что все зависимости зареганы в контейнере?
Anatoly
как ты проверишь на стадии компиляции, что все зависимости зареганы в контейнере?
Если с конструкторами? Довольно просто. Мне известны всё типы, которые мы резолвим, следовательно, можно их все попробовать зарезолвить
Roman
зарезолвить на стадии компиляции?
Anatoly
Да, я не вижу проблему это сделать во время сборки проекта
Roman
пост билд ивентом чтоль?
Anatoly
Да какая разница как? Мы реализацию что ли пишем? Например, пост билд эвентом, но возможно другим. Там много стадий, я на память не помню
Roman
ну это уже не стадия компиляции
Anatoly
Да даже на стадии компиляции можно
Roman
Это автомтизировано, так что это лучше, чем ничего
Anatoly
Метод резолв - он обычно один
Anatoly
ну это уже не стадия компиляции
А есть сильно большая разница, в какой стадии сборки это получать? Для конечного программиста
Vasily
С моей точки зрения, подобные подходы только добавляют сложности, к сожалению
Aleksandr
А как бы нам тогда в модуль регистрации зависимостей прокинуть конфиги?
Anatoly
Вон, в 3.0 завезли вроде проверку
Vasily
Короче
Vasily
С этими DI, конечно, играться можно
Aleksandr
А как бы нам тогда в модуль регистрации зависимостей прокинуть конфиги?
Его единственным что ли мокать придётся? А потом поддерживать это как-то
Vasily
Но проблема возникает, когда необходимо что-то переписать
Vasily
Ща мне, канеш, начнут говорить, что надо правильно проектировать
Anonymous
та тогда уже лучше все зависимости явно в конструкторе принимать и не выебываться. никаких тебе IEnv, а просто явные зависимости. если их много - то сгруппировать в рекорд, да и все.
Vasily
Но, к сожалению, правильное проектирование с первой итерации - оксюморон
Aleksandr
ahaha waterfall go wooooshhh
Vasily
Но у меня область специфическая - надо быстро и чтобы работало
Roman
Но проблема возникает, когда необходимо что-то переписать
у меня весь AppEnv с чтениями из конфига и object expressions занимает 130 строк очень простого кода.
Vasily
Особо времени на архитектурные изыски нет
Roman
интерфейсы к нему еще 20 строк
Roman
остальные чистые
Vasily
Ну не знаю
Vasily
Короче, вопрос холиварный
Roman
ну как хотите
Vasily
@atsapura , ты просто придерживаешься убеждения, что надо по максимуму формализовывать все, что есть, кмк. У меня жизненный опыт говорит о другом
Roman
почему все?
Stanisλav
С какого сообщения читать про тайпклассы vs DI?
Roman
Я всего лишь пытаюсь упростить себе жизнь. Мне проще 1 раз подумать подольше и написать предельно говорящий и явный код, чем побыстрее что-нить пушнуть в мастер сейчас, но потом ковыряться в логах в поисках ответа на вопрос "как возникла такая хуйня" и "почему тут отъебнуло"
Roman
С какого сообщения читать про тайпклассы vs DI?
не было про тайп классы покашто, но давай, набрасывай
Stanisλav
не было про тайп классы покашто, но давай, набрасывай
Чтобы набрасывать, надо мнение иметь, а я как раз хотел послучать
Roman
ну в фшарпе сегодня тайпклассов нет, так что сорян. Со скалой/хаскелем у меня опыта нет, поэтому я и тайпкласс-то в руках не держал
Vasily
Просто аргумент "чтобы все можно было протестировать" - он такой себе
Vasily
Благими намерениями, как известно
Vasily
Сам знаешь куда дорога вымощена
Roman
это все слишком абстрактные философствования. Было бы применимо, если б я замутил лютый овер инжиниринг во имя чего-то маловероятного. А так у меня код прост как 5 копеек, и его вряд ли даже больше, чем ушло бы на регистрацию компонентов
Roman
т.е. я пока не услышал даже конкретных недостатков подхода кроме "ну такое себе" и "по-мойму колесо"
Vasily
Я всегда задаюсь вопросом, насколько такая формализация усложняет разработку
Vasily
Ну т.е. сколько там бойлерплейта, в котором надо разобраться, как оно работает
Андрей
бойлерплейт спрятаный в библиотеку не перестает таковым быть, плюс все "течет", все "изменяется"
Roman
Ну т.е. сколько там бойлерплейта, в котором надо разобраться, как оно работает
type ICatalogDb = abstract member GetProductItem: string -> Async<ProductItem option> abstract member GetItems: string seq -> Async<ProductItem[]> abstract member GetPrices: string seq -> Async<ProductItemPrice[]> abstract member GetInventories: string seq -> Async<Inventory[]> type IProductBlob = abstract member SaveProduct: ProductId -> Localized<LocalizedCompleteProduct> -> Async<Result<unit, exn>> type IAppEnv = abstract member CatalogDb: ICatalogDb abstract member ProductBlob: IProductBlob abstract member Logger: ILogger abstract member Config: CatalogConfig abstract member ActorSystem: ActorSystem abstract member UpdateRouter: IActorRef<UpdateRouterMessage> abstract member UtcNow: DateTimeOffset
Vasily
Ну вот уже как-то дохера
Vasily
Хотя, возможно, я просто ничего в этом не понимаю
Андрей
конфиги на типклассах это примерно то, что описывает Роман. Дают больше уверенновсти уже при компиляции, и к тому же короче того что возможно в дотнетах
Roman
Ну вот уже как-то дохера
Лол. Нуштош, не буду больше настаивать
Vladislav
Здрасьте
mark
Привет
Roman
Потом все зависимости создаёте object expression'ами, в composition root передаёте appEnv, а дальше в нижележащие функции уже конкретные, например, appEnv.CatalogDb ? если не нужен весь appenv
let catalogDb = { new ICatalogDb with member _.GetProductItem itemId = getFromCosmos productItemToDomain itemId member _.GetItems ids = async { let! docs = CosmosCatalogDb.getItems cosmosClient ids return docs |> Array.map productItemToDomain } member _.GetInventories ids = async { let! entities = CosmosCatalogDb.getInventories cosmosClient ids return entities |> Array.map inventoryToDomain } member _.GetPrices ids = async { let! docs = CosmosCatalogDb.getPrices cosmosClient ids return docs |> Array.map priceToDomain } }
Vasily
Однако. бойлерплейт подъехал
Николай
Тут ведь из бойлерплейта по сути только создания объекта. Функции-мемберы в любом случае где-то будут реализованы. Либо сгруппированы в одну зависимость как здесь, либо где-то в модуле, либо где-то ещё
Roman
Да Василий как всегда