Как стать автором
Обновить
72
0
Tishka17 @Tishka17

Пользователь

Отправить сообщение

Достаточно часто встречаю часто такие подходы к разработке интерфейса инструментов:

  1. Делаем очень удобное апи для вырожденного случая. На остальные забиваем. Шаг в сторону от этого кейса - выкидывать библиотеку/инструмент

  2. Делаем всратое апи, чтобы любой кейс покрыть. Не пытаемся никак приоритизировать ситуации. Человек мучается даже с теми вещами, которые делает постоянно.

  3. Делаем удобное апи для плохих архитектурных решений, сильно более сложное для хороших. Зачастю документация тут покрывает как раз только удобную часть. Человек выбирает плохие решения, так как они проще.

Рассматривали ли вы использование aiogram-dialog? Кажется, для вашего кейса подошло бы неплохо

Универсального "подхода" нет, есть универсальные принципы, они достаточно абстрактны и гибки. А есть ещё универсальные антипаттерны - их особенность в том что они не решают никаких проблем (кроме проблемы "мне не нравится другой подход"), а потом сами становятся проблемами.

  1. Интеграция слоя работы с БД со слоем фреймворка прекрасно делается с помощью DI-фреймворков. А в иделе вообще её нет, так как к БД вы обращаетесь из бизнес логики, а не фреймворка. Это позволяет сделать код гибче и более тестируемым

  2. Поддержка асинка в алхимии есть, не понимаю этот пункт вообще.

  3. Автоматическое построение моделей pydantic из моделей ORM - вещь немного проклятая. Сначала кажется крутой, но постепенно различия накапливаются, у вас появляется несколько версий модели пидантика. В конце концов вы все равно начинаете писать мапперы вручную.

  4. Поддержка несокльких субд как раз в тортойзе сделана хуже. Если у вас уже есть модуль работающий с БД, то вы не можете просто так взять и его юзать - он начнет обращаться не к той базе. В результате все такие модули надо параметризовать именем базы с которой они связываются, если вы действительно хотите их как-то независимо пилить (а иначе зачем вам несколько баз). Становится более похоже на прокидывание везде сессии алхимии только без её преимуществ

  5. Пробелма суждения "нужно только 80%" в том что вы не знаете какие именно 80%. Фреймворк должен уметь ~100%, ваш код - сколько вам надо. Менять фреймворк потому что понадобилась какая-то мелкая фича - неконструктивно

Не читал Clean Code, но в Clean Architecture всё достаточно банально и логично. Что же касается любых книжек с примерами чего угодно - синтетические примеры не обязаны отражать реальность, они должны быть пригодны для использования в нужном аспекте. К сожалению, я очень много сталкивался с критикой вида "Х дурак, потому что в примере Y он сделал плохо Z", в том время как в примере Y вообще не надо обращать внимание на Z, он про дргуие аспекты. Я так же слышал много критики вида "если везде применять Y, описанный в книге, получится плохо" - это тоже не вполне корректная вещь, есть методы которые приенимы везде, есть методы которые применимы в ограниченном числе случаев. Уметь отличать их - тоже часть профессии и я не думаю, что 100% формализация применимости всегда возможна

Ну я повозился с этим Stub и сделал dishka, который прекрасно можно юзать с FastAPI.

register_new, register_removed, register_dirty? У Фаулера список методов указан же

я переформулировал: вопрос в том, мы вычисляем абсолютную величину для целого или для дробного числа.

ну и в порядке шутки из других языков:

>>> x=-999999999999999999999999999999999999999999999999999999999999
>>> sqrt(abs(x))  
1e+30
>>> sqrt(fabs(x))
1e+30

используем синтаксис языка Си, хотя язык здесь не важен

Пишу на языке где нет перегрузки функций по типу параметра, поэтому - важен.

Добавление и редактирование карточки товара актуально для карточки товара. А, например, редактирование проведенной транзакции - вещь странная. Редактировать сумму счета вообще не стоит просто так. Если вы пишете конкретную логику, там будут конкретные действия вроде "отредактировать карточку товара" (редактирование даты создания сюда не входит) или "поменять статус транзакции" (изменение суммы перевода сюда не выходит). Эти действия формально все udpate, но при этом они не абстрактные, они делают конкретный вид апдейта, имеющий определенный смысл с точки зрения бизнес логики. И другие апдейты при этом могут быть запрещены

Не делать CRUD вообще. Этот набор букв - вырожденный случай. В реальном мире у вас не CRUD, а что угодно - начиная от только R и заканчивая десятками действий на сущность.

В чистой архитектуре не упоминается нкиакой CRUD и сервисы. В ней мы проектируем отталкиваясь от сценариев использования. Интеракторы - это конкретные шаги сценариев, они уже вызывают вспомогательные классы (клиенты апишек, хранилище, "сервисы"), интерфейсы которых проектируются исходя из реальных требований конкретного сценария.

Даже если рассматривать только R(ead) - мы имеем разные варианты поиска сущностей. Где-то чиатем по ID, где-то получаем отфильтрованный список, где-то динамические фильтры зависящие от пользователя. С U(pdate) всё ещё хуже. Продукт может быть продан, деньги переведены на другой счет, дом построен. Сгребая всё под термин update мы теряем самое главное - то ради чего писался код, бизнес value конкретной единицы.

Обобщенный репозиторий может быть. Точечно или закрытый "фасадом". В общем, не стоит начинать с него.

Протоколы, лежащие отдельно это окей. Но они принадлежат тому же слою что интеракторы. И нужно как-то понимать где граница этого слоя.

Что за CRUDService? Какое он место занимает на исходной схеме? Что по Interface Segregation Principle?

Те же вопрос к CrudRepository. Почему это дженерик класс? Почему в нем именно такие сигнатуры методов? Гарантируете ли вы что для все сущностей все методы должны быть именно такие? Например, не везде id будет int, мало где нужен fetch_all, зато много где будут разные способы фильтрации. Почему у вас протоколы репозиториев лежат где-от отдельно от интеракторов (я же правильно понял что их роль выполняют функции в последнем блоке кода типа sell)?

Рекомендую к прочтению https://www.ben-morris.com/why-the-generic-repository-is-just-a-lazy-anti-pattern/

Если у меня есть запросы вида get by id, list с различной фильтрацией, то я хочу чтобы полученная сущность была одного типа. Желательно того, который я спроектировал до того как начал детализировать логику запроса.

Джойн не меняет сам по себе состав полей результата, он может использоваться например для фильтрации по связанным данным, а список полей при этом быть такой же как без джойна.

Я правильно понял, что вы предполагаете сгенерировть кучу одинаковых классов и если мне дальше надо результат таких запросов одинаково преобразовывать - то делать union?

Тип сгенерирован и реален: делайте с ним, что душе угодно.

а если у меня есть уже тип, который я определил при проектировании бизнес логики, как мне заставить эти функции его возвращать?

Лично мне от слоя работы с БД мне нужно две вещи: возможность легко собирать в нем разные запросы и маппить результат в какие-то классы. И тут я вижу два варианта: эти классы определены в слое работы с БД как ORM и у вас и тогда не понимаю чем плох ORM, или они определены в доменном слое и тогда я не понял как будет маппинг на них

Спсибо большое, я понял о какой проблеме речь. То есть мы теперь мы

  • потеряли:

    • возможность из линтера (без живой БД) частично проверять, что поля в запросе соответствуют некоторой определенной в коде модели

    • возможность динамически собирать запрос

    • переиспользовать типы, определенные вне слоя работы с БД

  • получили:

    • генерацию типов, соответствующих реальной БД и реальным запросам

так?

Каюсь, просмотрел по диалогонали. Увидел: написанный текстом SQL и тезис "Никаких SQL билдеров, никаких ORM, никаких моделей - просто запросы и их результаты в любой удобной для проекта форме". И вот непонимаю, елси это дополнение к SQLAlchemy Core, то почему запросы текстом. А ещё тезис, что линтер как-то поможет тут - какой линтер проверит что строковые тексты запросов соответсвуют структуре БД?

Ниже вы подставляете имена колонок из таблиц ORM, но в этом случае непонятно зачем вообще это писать в таком виде. Мы лишаемся возможности динамичеки формировать запросы, а получаем что? Очередную библиотеку маппинга на датаклассы, но почему-то привязанную к СУБД?

А какие проблемы Sqlalchemy Core решает этот проект?

Рекомендую автору почитать зачем нужен dependency injection, dependency inversion и зачем люди делают зависимости более явными (спойлер: чтобы код работал более очевидно). Вы соорудили какого-то монстра чтобы сэкономить на передаче сессии, зато потом непонятно как это поддерживать.

Зашёл прочитать про код без типов, а прочитал про код без тайп хинтов.

Автор, не путайте нас. Когда вы пишете x=1, число 1 имеет тип int, а вот переменная x хинта явного не имеет. А дальше можно накручивать разную логику подсказок и проверок. Но факт в том, что тип у объекта есть и доступные операции у единицы как раз определяются ее типом.

1
23 ...

Информация

В рейтинге
5 192-й
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Backend Developer, Mobile Application Developer
Lead
Python
Docker
Linux
SQL
Git
Golang
Android SDK