Search
Write a publication
Pull to refresh

Comments 18

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

Софт без задефайненных non-functional requirements, quality attributes и код без тестов это прототип. На этом уровне техническая задача обычно сводится к тому, чтобы решение лишь бы хоть как-то работало. Работает? - вы молодец. Но дальше в отсутствии требований об архитектуре сервиса говорить нет смысла потому, что архитектура выбирается исходя из требований. Вы же понимаете, что допустим для достижения availablity 99.9 и 99.999 у вас будут два принципиально разных архитектурных решения и разницей по сложности и стоимости примерно на один-два порядка.

Но проблема даже не в этом. Принимая ФИО, паспортные данные и т.п., с точки зрения законодательства вы становитесь оператором персональных данных, со всеми вытекающими требованиями - к хранению, обработке и передаче. Я не увидел даже намека. Вы хотели отзыв: десять лет тому назад это выглядело бы даже неплохо (все эти базворды типа onion architecture, DI и т.п.). Но в 2023 предложенное решение - детский сад штаны на лямках - в решении уровня лида хочется все же видеть больше осмысленности.

Спасибо за первый комментарий к первой статье.

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

Согласен с отсутствием нефункциональных требований и метрик качества. Сформулировать сейчас не получится, но в одном из разделов указал на скорость ответа базового сервиса от налоговой. При "развитии" сервиса попытаемся сформулировать и эти требования.

Про тесты написано в последнем разделе. Предвосхищая ваш вопрос почему сначала не пишем тесты, а потом код - так не было запроса на следование TDD.

как вы сказали, на устаревших базвордах луковичной архитектуры и контейнеров.

Я ни где не говорил, что это устарело. Скорее развилось и стало рутиной. А вот подходы к работе с PII поменялись радикально из-за необходимости соблюдать кучу юридических церемоний. В вашем решении данные не классифицируются, свободно передаваясь между компонентами. Поэтому с предложенной архитектурой бизнес влетит на мешок денег после первого же compliance report. Чтобы этого не произошло, такие требования нужно закладывать сразу. Попробуйте, вы сразу увидите как это заставит перекроить всю архитектуру.

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

Что касается тестов, то писать их до или после это тактика. Главное, чтобы в конце они появились. :) Ну и линтер с тайпчекером прикрутить к CI вроде уже не экзотика.

Двоякое чувство от вашего комментария, с одной стороны вроде как по делу, если мы говорим о неком продовом решении, или как ответ на конкретное ТЗ, с другой стороны нигде не заявлялось, что в статье будут рассмотрены данные аспекты:

«Основная идея статьи — поделиться опытом работы с Python в части создания законченного проекта с использованием контейнера зависимостей, создания слушателей для RabbitMQ и работой с базой данных MongoDB.»

Возможно часть дискуссии ведется вне данной площадки, и я встреваю не в чужой диалог, но ради интереса посмотрел, о чем вы писали "лет 10 назад".

Честно говоря, двоякое чувство от вашего комментария. С одной стороны вроде как по делу, если мы говорим о некотором конкретном пользователе, с другой стороны не придумал пока)

Я правильно понимаю, что все настройки у вас запихнуты в класс Settings? Насколько это хорошая практика?

И по поводу логгера — имеет ли смысл его прокидывать каждый раз отдельно, а не использовать глобальный? В какой момент вам может понадобиться его заменить чем-то другим?

Да, все настройки приложения в Settings. По логгеру можно и без DI обойтись тем более, как правильно заметили, никто его заменять не будет. Даже в тестах )) Сделано в одном стиле.

По настройкам я всё же предлагаю примерно такую структуру, чтобы можно было что-то прокинуть отдельно. Если есть что добавить — буду рад. :)

Код
### settings.py
 
from functools import lru_cache
 
from pydantic import BaseSettings
 
 
class DroneConfig(BaseSettings):
    token: str
    api_url: str
    namespace: str
 
    class Config:
        env_file = ".env"
        env_prefix = "drone_"
 
 
class Config(BaseSettings):
 
    drone: DroneConfig = DroneConfig()
    ...
 
@lru_cache
def get_config() -> Config:
    return Config()
 
 
### tools/drone.py
 
from aiohttp import ClientSession
 
from settings import DroneConfig
 
 
class DroneService:
    """Drone service class for making requests via Drone API"""
 
    def __init__(
        self, aiohttp_client: ClientSession, config: DroneConfig
    ) -> None:
        self.api_url = config.api_url
        self.namespace = config.namespace
        self.client = aiohttp_client
        super().__init__()
 
    …
 
### some_service.py
 
…
 
drone = DroneService(
    aiohttp_client=DroneClient.get_aiohttp_client(
        config=get_config().drone_config
    ),
    config=get_config().drone_config,
)
await drone.some_method()
 
…

Спасибо за идею, логично выглядит. Лишних настроек нет в классе. Вроде как в последних версиях pydantic появилась. Внешнего немного сложно и громоздко получается за счёт префиксов. Используем такой способ для передачи структур, но что-то для ограничения видимости настроек не догадались применить.

  1. всегда удивляло, почему используют повсеместно @lru_cache def get_config() -> Config: return Config()

вместо более "питонячего"
config = Config()

another_module.py

from settings import config

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

  1. Но вы же знаете, зачем используется @lru_cache?

  2. Не понял — вопрос к префиксам или к .env файлу? .env файл используется, если в переменных окружения ничего не нашлось — то есть я не вижу озвученной вами проблемы.

  1. кэширование? для того, чтобы не делать снова вычисления. т.е. в этом случае первые 128 раз у вас вернется уже вычисленный объект. но если этот объект вычислить "на месте" и потом уже использовать его инстанс, можно обойтись без кеширования. оно, конечно, зависит от сценариев использования, но обычно настройки нужные всем частям программы одинаковые, и если они (зачем-то) меняются в одном месте, они сразу меняются везде. в случае кэширования такой паттерн не работает. не говоря о том, что одна строчка короче трех

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

Неплохое начало, жду продолжнения. ;o)
И хотелось бы все же увидеть планируемую архитектуру.

к автору тоже вопрос, что дает использование контейнера, почему объекты именно синглтоны, почему провайдеры?

почему нельзя было использовать создание объектов в одном модуле и импорт их дальше по коду. теже синглтоны получаются. зато никаких внешних библиотек.

Контейнер с зависимостями можно подменить для тестов. В тестах будет другой набор зависимостей, где-то фейки, где-то заглушки.

заинтересовала вот эта тема "включение глобального обработчика ошибок."
как именно это реализовано? в модуле запуска видно, что исключения BaseException перехватываются, но никак не логируются. в чем глобальная обработка?

опять же понимаю, что это скорее прототип, чем рабочее решение, которое можно выпустить в прод, потому что скажем, в InnService, делается запрос в репозиторий, но если внутри прозойдет ошибка, то она никак не будет обработана, и получается, что сообщение просто потеряется?

Глобальный в том плане, что не перехвачено внутри сервиса и обработчиков, то будет перехвачено в "глобальном".

Да, вот как раз на подходе развитие статьи и доработка таких вот ошибок. Обработка ошибок в очередях довольно сложная штука потому как мы можем выполнить частично обработку, затем в момент ack сообщения потеряем связь с RabbitMQ. В такой ситуации надо делать идемпотентным обработчик, проверять запись на предмет её повторной обработки.

Sign up to leave a comment.

Articles