Обновить
60
0
Игорь@CodeShaman

Программист

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

С подключением из глобально инициализированного пула внутри get_users в большинстве случаев нет проблем по моему опыту. Инициализировать переменную окружения мы точно не забудем о чем нам скажет завалившийся деплой. Модуль settings просто не найдет инициализированную переменную и старт приложения завершится с ошибкой.

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

Если источники очень разного характера, к примеру SQL и HTTP, то получение данных из них в любом случае лучше разнести по разным функциям. Если одна и та же сущность в одинаковом виде лежит в разных базах для одного и того же приложения, то это скорее повод подумать, все ли в порядке с хранением данных. Обычно данные об одной и той же сущности лежат в разных источниках, если это все же данные разного характера. В случае с Post мы можем получать данные о просмотрах из какого-нибудь кликстрима, вроде Кликхауса. В общем случае работа с разными источниками будет реализована через разные интерфейсы на уровне непосредственного получения данных. Ведь, собственно, за абстрагирование работы с этими разными интерфейсами и отвечает слой persistence. В него мы получаем что-то из слоя infrastructure, вроде коннекта к конкретной базе или HTTP сессию, а уже функция внутри реализует всю работу по извлечению данных через АПИ этого источника и оборачивание в доменную сущность, чтоб вернуть в ее бизнес логику.

Хм, касательно второго думаю, что относительно да, до тех пор, пока в условном классе Users не идет обращение к самому экземпляру внутри методов. :) То есть, пока класс выступает по сути в роли неймспейса для всех этих функций aka методов для из группировки.

Все таки, мне кажется что основная задача наличия класса - это инкапсулирование работы с объектом по изначальной задумке. Найти функцию в модуле и найти метод в классе в целом задачи +- одинаковые по сложности. Сначала нужно найти нужный модуль или класс, потом найти в нем нужное поведение. Ну и доступ к функциям в модуле через IDE можно упростить сделав import functools и потом IDE выдаст через точку все содержимое модуля полностью аналогично с содержимым экземпляра класса. Можно даже написать алиас, если хочется меньше в коде писать символов import functools as ft. Тут главное, чтоб конвенция была для алиасов модулей. Впрочем, как и нужна конвенция для именования переменных и так далее.

выяснится, что функции работы с, например, постами нужны вместе, вместе с необходимыми dataclass, и в неймспейсе вида Post

Тут не очень понял. По идее Post - это доменная сущность и ее объявление, как и правила создания мы кладем в доменный слой нашего приложения. Который не зависит от других слоев. А уже на уровне сервисов мы выстраиваем работу с Post с помощью тех функций, которые реализуют нашу бизнес-логику. Там и встречаются данные и поведение.

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

Тут коротко просто отвечу той же мыслью, что высказана в статье. В Питоне нет по настоящему ничего приватного. "Все мы здесь взрослые, отвественые люди". Если кто-то захочет куролесить, изменяя объект так, как этого делать не следует, он всегда сможет это сделать. Такие вещи только код-ревью решаются, так или иначе. Ну и дисциплиной и ответственностью самих разработчиков, опять же так задуман язык его создателем. Тут оно, как есть :)

Отказа от ООП как такового нет, все верно. И об этом даже есть не большая цитата от Роберта Мартина на тему того, что ООП и ФП могут и даже должны хорошо уживаться в современных системах. Отказ от так называемого классического ООП, которое по большей части получило широкое распространение благодаря Java. Это самое ООП может быть очень разным, о чем тоже есть пример в статье - JavaScript.

Никакого объединения данных и методов с помощью неймспейсов не декларируется. Как раз наоборот, данные существуют отдельно. То, что их обрабатывает существует отдельно. И фундаментально проблемы в этом нет, опять же о чем есть пара слов в статье. В ФП языках в принципе не стоит этот вопрос об объединении данных и логики в одну корзину. Что может работать с чем определяется сигнатурами функций и интерфейсами.

Так как я не работал с C#, то могу судить только из быстрого ознакомления с предметом по описанию.

Если я все правильно понял, то default interface methods можно примерно похоже реализовать с помощью модуля abc https://docs.python.org/3/library/abc.html

По поводу методов-расширений первое, что приходит на ум - это monkey patching.

В данном случае нет. Объект Post содержит в одном из своих свойств массив комментариев. Функция возвращает нам массив не прочитанных комментариев. Конкретно эта функция является чистой и не имеет побочных эффектов и ее выходные данные зависят только от входных. Передавая один и тот же объект Post на вход мы всегда будем получать один и тот же результат на выход.

Спасибо. Тут скорее не то, чтобы очень свежий взгляд, а больше желание передать опыт тем, кто начинает разрабатывать системы. И постараться пролить для них свет на те вопросы, которые вставали передо мной и, возможно, встанут перед ними.

Не очень понятно выходит тогда, а как предлагается решать такую проблему? Кажется, что наличие или отсутствие модуля persistence не убирает и не добавляет фундаментальную проблему наличия работы с источниками. И их интеграционного тестирования.

Да, благодарю за замечание, поправил. Конечно же по тексту верно:

Мы знаем, что метод get_unread_comments работает с экземпляром класса Post

По поводу:

Можете прояснить, не совсем понятно, в какой именно момент это стало ясно? Мы вроде
изначально могли раздельно все хранить

Согласно классической концепции ООП, объект содержит поведение или методы. Нам необходимо определить, какое поведение соответствует каждому объекту и встроить его в объект. Однако, в предыдущем тексте я постарался показать, что не обязательно решать эту задачу и старательно определять, в какой объект мы должны вложить поведение. Во многом упрощает многие аспекты работы над программой.

Вот тут немного не понял вопрос. :) Если можно, как-то развернуть его подробнее.

По поводу ROP в Python, к сожалению, хорошего ответа пока нет. Говоря о ROP, больше хотелось показать само наличие концепта и идей, в него заложенных, как пищу для размышлений и расширения кругозора. Сейчас очень интересует попробовать на практике возможности https://returns.readthedocs.io/en/latest/pages/railway.html# , но пока есть некоторые сомнения в лаконичности и естественности такого кода в Python. Это еще предстоит посмотреть и оценить, само крайне интересно, что получится. Вариант делать по Go-шному и всегда из функций возвращать значение и ошибку возможен, но в целом тоже не то, чтобы очень хорошо выглядел в Python. Пока как базовую хорошую практику взято за правило не использовать инфраструктуру фреймворка внутри бизнес-логики для работы с ошибками. То есть, после выброса ошибки, она должна быть поймана в именно хенделере, который вызвал конвеер, и выбор ответа должен произойти там же. Чтобы все заранее запланированные пути завершения работы были видны из точки вызова. Конечно, расстраивает то, что такая штука держится исключительно на конвенциях и дисциплине разработчиков. Ведь в случае с F# и ROP разработчика ударит по рукам компилятор, если какой-то из путей штатного завершения программы не был обработан. Тут мы, к сожалению, просто снова возвращаемся к фундаментальным проблемам работы с бизнес-логикой в языках, где нормальным является выброс исключений, и их отлов уже где-то на верхних уровнях в коде.

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

Отдельной Open Source базы, к сожалению, нет, где можно было бы посмотреть полноценные реализации. Весь код такого характера пишется именно на работе. Хотя понимаю суть запроса - увидеть больше деталей было бы полезно. Я взял на заметку на будущее, хотя в целом суть данной статьи была не столько до косточек разобрать, как именно писать конкретный микросервис во всех тонкостях, сколько о том, как стоит работать с информацией, изучением книг и отдельных подходов для того, чтобы избежать фрустрации, когда какая-то теория на практике не очень хорошо работает в вашем коде.

Не совсем понятно, для чего именно здесь нужна ORM. В данном случае вы создаете объект Publication и передаете его в get_unread_comments. Объект Publication не связан с ORM. Вам не обязательно его брать из базы. Вы можете создать его в коде теста перед вызовом функции.

И если мы говорим о работе с базой, то это уже не юнит-тесты. Это интеграционные тесты, которые тоже очень полезны и хороши. И при правильно настроенной инфраструктуре они работают не очень сложно.

Допустим, в рамках деплоя через CI/CD на этапе тестирования вы можете просто поднять рядом в докер-контейнере экземпляр используемой вами базы. Да, для такого тестирования у вас должен быть механизм подготовки базы перед запуском теста, механизм отката базы в дефолтное состояние после завершения теста. Обычно сам механизм вы делаете и настраиваете один раз при начале работы с интеграционными тестами. А дальше дополняете или файл базовых миграций для подготовки вашего дефолтного экземпляра базы или как-то в самом интеграционном тесте, если вам нужно создать в базе что-то специфичное для конкретного случая.

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

А можно, немного более полно раскрыть комментарий? Пока не очень понятно, о каком именно захаркоженном глобальном контексте идет речь и почему написание юнит-тестов превращается в боль.

Если мы говорим о написании любого классического юнит-теста, то, тестируя функцию, нам в любом случае придется подготовить входные данные согласно сигнатуре этой функции. В данном случае нам нужно инициализировать экземпляр объекта Publication и передать его в get_unread_comments.

Опять же, тестирование в целом - это большое благо. И если есть время тестировать каждую функцию отдельно такими юнит-тестами, то это классно. Но иногда можно протестировать интеграционно готовый воркфлоу по всем основным бизнес-сценариям. Положительным и отрицательным, где каждая такая встроенная функция пройдет тестирование в рамках другой функции, куда она встроена, в рамках бизнес-процесса.

Обычно хватает фраз такого плана после очередного сгенерированного им блока: "продолжай", "продолжи с фразы ...последняя фраза в предыдущем блоке...", "продолжай и добавь примеры кода на Python" и так далее

Признаться честно, эта мысль меня посетила самой первой. Опубликовать статью, которую написал chatGPT и посмотреть, что из этого выйдет. Но потом я подумал, что это в целом как-то не очень этично и как Хабр отнесется к такой "выходке". Ну, и что потом сделает за это моему аккаунту :)

Пожалуйста!

Следующий скрин: 7 кнопок в углу виджета :)

Это все элементы управления, которые в обычном режиме скрыты от глаз пользователей и появляются только при ховере :) Написанное про нагрузку все же больше про то, что чем более похожей будет визуализация на то, с чем годами работал пользователь, тем ему будет проще эффективно визуально считывать информацию и освоиться в новом инструменте. А потом постепенно можно уже что-то менять.

Как вы поняли что нужно пилить свою систему, а не выбирать из уже существующих на рынке вариантов?

Об этом по сути написанна первая статья. Но если коротко, то были опробованны другие популярные решения на этом рынке, но не одно до конца не устроило ни конечных потребителей, ни команду аналитиков.

Насколько это оказалось дешевле или дороже промышленного решения в итоге?

Тут скорее не по моему профилю вопрос, я все таки разработчик. Могу только сказать, что главным критерием выбора системы был не финансовый вопрос, а именно необходимость удовлетворить потребности конечного потребителя продукта в компании.

Все таки программирование в школе на Delphi в рамках факультативного интереса - это не уровень junior-программиста. Программирование на Delphi для меня было интересным хобби. К моменту принятия решения сменить профессию с тех пор прошло столько лет, что все фактически пришлось осваивать снова с нуля. Этот факт из моей биографии отражает скорее то, что мне всегда была интересна эта сфера, но реальные знания в сфере IT я начал получать именно когда решил сменить профессию.

На тот момент я уже был женат и моя супруга была моей опорой во время этих перемен. Дети появились уже после. С совсем маленькими детьми было бы крайне трудно по наличию свободного времени, с подросшими уже проще.

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

С .NET не работал и не знаком, но с большим интересом иногда факультативно интересуюсь F#. Интересно, каково его будущее в .NET, вакансий для него практически нет, но вроде Microsoft продолжают его развивать и какие-то фичи из него перекочевывают в C#.

Обещать не буду, но возможно что-то потом напишу.

Информация

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