Обновить

Комментарии 10

Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще? 

  1. Единая ответственность. Каждый сервис реализует законченный кусок бизнес-логики — от простого создания объекта до комплексных операций с вызовами других сервисов или внешних API.

  2. Единственный публичный метод. Вся работа с сервисом ведётся через метод call. Избавляемся от проблемы когда разработчики начинают придумывать run-ы, execute-ы, process-ы и т.д.

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

Единственный плюс, который вижу у подхода автора - что для входящих данных отдельный dto не нужен (т.к. уже есть init у dataclassa).

У класса есть как минимум 2 преимущества:

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

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

Благодарю за комментарий, как с языка сняли

Если Вам нужен класс с одной функцией- это значит что класс здесь в принципе не нужен.

Спасибо за комментарий! Возможно, тут возникло недопонимание - в классе может быть несколько методов, просто публичный - только один - и это метод __call__.

старались, насколько это возможно, не изобретать велосипед

и изобрели... функции

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

Ещё есть аргумент против использования __call__ - если не ошибаюсь с ним невозможно вызвать goto-definition в месте вызова метода, чтобы перейти к определению __call__

Подскажите, пожалуйста, что вы имеете ввиду когда пишите «функции»? В нашем случае класс может содержать > 1 метода, просто один публичный и по умолчанию это метод __call__. Возможно, из статьи это не очевидно, хотя я приложил примеры с абстрактным классом, где используется шаблонный метод.

По поводу goto-definition - справедливое замечание, спасибо

Нельзя чтобы ваш сервис возвращал:

  • Словарь/список/кортеж и тем подобные структуры.

Вместо этого следует использовать DTO для возврата структурированных данных:

Вспоминая, что DTO - любая структура данных без логики, которую можно сериалзовать и передать по сети, я не понимаю почему вы считаетете что list[int] или tuple[int, int] - это не DTO. Идея описать выходные данные и дать им структуру - хорошая, но если вы хотите вернуть просто список, почему бы не вернуть его как етсь?

Тут могут возникнуть другие рассуждения в духе "одного списка никогда не хватит, появятся метаданные" и они имеют смысл, но вы же вообще не про это

Писят два миллиона строк... Страшно представить... Это же как два ядра Линукс в сумме. Точно писят два? Не просто "два"?

Ну а по теме скажу так, мы тоже пробовали такой подход и нам не зашло. Из проблем было: проблемы переиспользования сервисов, глубокая вложенность сервисов (когда один вызывает второй, затем третий и в итоге - лапша), большой шанс дублирования и нелепых попыток его избегания, когда связанность проекта растет как на дрожжах.

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

Мы в итоге остановились на более гибком подходе - Feature slice. Эт когда для каждой фичи есть блок "сервисов", который более гибок чем стандартный шаблон одного класса(с точки зрения организации кода), но при этом сервисы не могут переиспользоваться между фичами. Таким образом high cohesion достигается из той жёлтой книжки из превью к статье. А вот переиспользуемый код уже как раз в функциональном стиле и он пишется в отдельных блоках.

Честно говоря - это не просто. Обилие логики всегда порождает связи, которые потом влияют на развитие, мешают вносить изменения и "ломают то, что мы даже не меняли*. Кажется, это проблема больших систем в целом, а не подхода в частности. Одно из решений - повальное дублирование и жёсткие контракты (формата HTTP API). Но микросервисами такой подход попахивает, а ими мы тоже уже наелись.

Что дальше? Может быть модульный монолит где каждый модуль - отдельная фича? Может быть...

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации