Go Clean Template | Чистая Архитектура и как её готовить, Михаил Непряхин

Поделиться
HTML-код
  • Опубликовано: 2 июн 2024
  • Подписывайтесь на наш канал здесь и в телеграмм t.me/meetups_evrone, чтобы быть в курсе будущих митапов и не пропускать полезные доклады!
    Михаил Непряхин, Evrone
    Как использовать Clean Architecture в микросервисах на Go. Как разработать свой шаблон используя принципы Чистой Архитектуры
    00:00 - Введение
    00:36 - Робер Мартин ака Дядя Боб
    01:43 - Какие проблемы решает Чистая Архитектура
    02:55 - Типичный микросервис на Go
    03:48 - Скорость разработки Роберта Мартина
    04:48 - Инверсия зависимостей
    08:14 - Поток управления
    08:41 - Авторская версия построения архитектуры
    17:33 - Инъекция зависимостей
    18:49 - Инъекция зависимостей в usecase
    20:16 - Инъекция зависимостей в controller
    22:22 - Нейминг в Go
    23:50 - Как разложить код по папкам
    25:05 - Изоляция доменной области
    26:42 - Про фреймворки
  • НаукаНаука

Комментарии • 78

  • @fess932
    @fess932 2 года назад +47

    наконец то первое нормальное видео которое прям по полкам про чистую архитектуру на го поясняет, ура, я столько пересмотрел видосов, и нескольок раз прочел мартина чистую архитектуру, но это видео прям глаза открыло

    • @user-up1ii7zt8y
      @user-up1ii7zt8y 9 месяцев назад

      да и на node js это легло отлично!! Просто браво!!

  • @RusFarFaz
    @RusFarFaz Год назад +8

    Большое спасибо за объяснение! Особенно с этим кругом было не понятно, а после вашего рисунок все стало на свои места.

  • @MrQsam
    @MrQsam Год назад +5

    Спасибо. Видео несколько раз буду пересматривать .

  • @shushard
    @shushard 11 месяцев назад +4

    Наконец-то кто-то нормально объяснил эту тему

  • @PavelKorchagin-tl5ff
    @PavelKorchagin-tl5ff 11 месяцев назад +12

    Спасибо за доклад, очень круто! Есть пару моментов с которыми я не согласен:
    12:00 - плохая идея использовать одну структуру в entity на все слои, объясню почему: мне нужно из controller передать в usecase два поля для пагинации (offset, limit). Почему слой entity (слой enterprise бизнес логики) должен знать про какую-то пагинацию?)
    14:20 - плохая практика использовать backticks с json в слое entity, т.к этот слой ничего не должен знать о представлении. То есть, если придет заказчик и скажет, что мне вместо json подавай xml, то придется менять слой entity (получается entity и controller имеют какую-то косвенную связь). К тому же у вас могут быть принципиально разные структуры, что в слое entity, что в controller.

    • @neprja
      @neprja 10 месяцев назад +1

      Разумеется. Но это уже детали. Я высокоуровнево и доступно старался объяснить.
      В реальном мире нужен пакет dto в котором это всё будет находится. Я dto использую для связи controller usecase. Большие и сложные dto бывает доходят и до адаптеров.
      Ну и внутри entity постепенно появляются другие пакеты по мере разрастания логики.
      Главное понять как управлять сложностью и где проводить границы между разными частями сервиса.

    • @kharakternyk_
      @kharakternyk_ 7 месяцев назад

      @@neprja а каким образом стоит декаплить пакеты, в случае если мы в юзкейсе реализовали проверку(errors.As, errors.Is) и хендлинг кастомных ошибок(созданных errors.New) , возвращаемых пакетом, отвечающим, например, за интеграцию с другим микросервисом. Мы будем его импортировать, чтобы иметь доступ к переменным и типам тех ошибок. Или есть альтеративный подход, где мы оперируем абстракцией?

  • @nnnabbot
    @nnnabbot 14 дней назад +1

    Спасибо за видео. Очень многое понятно. Но хочется для таких как я 😂(начинающих), объяснить все тоже самое , но на примере конкретного приложения , например файлообменник. И прям на нем показать данный подход

  • @andreychirkov1904
    @andreychirkov1904 Год назад +6

    Классный доклад! Очень доходчиво, структурировано и понятно. Спасибо

  • @spotlight2206
    @spotlight2206 10 месяцев назад +3

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

  • @dmitriyobidin6049
    @dmitriyobidin6049 9 месяцев назад +4

    В рамках микросервисов нет деления на понятние application logic и core business logic. Потому что в самом смысле микросервисов заложено, что микросервисы должны быть слабосвязаны между собой, а соответственно и сквозных данных/логики между ними должно быть по минимуму.
    Это разделение было для стандартных монолитов, и за счет этого разделения возможно было бы использовать один единый удобный слой core business logic для разных целевых модулей/блоков приложения. Например, одна модель счет фактуры для разных сценариев и интерфейсов использования: наборщик на складе, водитель-экспедитор, приемщик и т.д. Они работают каждый через свой слой абстракции(application logic) с одной общей моделью(core business logic).
    В архитектуре микросервисов это спокойно распиливается на 4(условно) микросервиса.

  • @kitusis244
    @kitusis244 2 года назад +2

    Огонь! Спасибо большое!

  • @dmitriyobidin6049
    @dmitriyobidin6049 9 месяцев назад +3

    19:17 Разве в импортах не должно быть хотя бы импорта моделей/entity?

  • @TheArtofDevelopment
    @TheArtofDevelopment 2 года назад +18

    Классное объяснение! Заберу к себе пару тезисов - у меня на канале реализация чистой архитектуры в 3 частях.

  • @user-gx6mq9fz9q
    @user-gx6mq9fz9q 2 месяца назад +1

    Подскажите, а как быть, если нужно в одном юзкейсе вызвать методы другого юзкейса ( в apple дернуть метод, связанный с banana)?

  • @tumenit
    @tumenit 2 года назад +2

    Спасибо!

  • @dmitriyobidin6049
    @dmitriyobidin6049 9 месяцев назад +4

    Тоже никогда не понимал, зачем нужно пилить столько dtoшек в рамках микросервиса, особенно при том что всё это делается в папке internal, т.е. переиспользование всего этого добра в других проекта не предполагается...
    По мне так dto в такой архитектуре должен быть аналогом адаптера типов. Т.е. если вместо одного из модулей или вместо одной из частей системы мы подключаем внешний пакет, который не умеет работать с нашими моделями. В таком случае мы как раз используем dto, чтобы произвести маппинг на нужный формат данных. Но если мы все пилим в локальных, закрытых, не нацеленных на переиспользование в других проектах модулях - нафига этот огород?
    Прокидывать везде единые модели данных - это нормально. Но при этом если потом понядобится/захочется ввести локальные dto для каких-то сервисов/модулей - это можно легко сделать точечно.
    Конечно же все вшесказанное работает, если dto и entity совпадают на 90+%. Когда мы там передаем кучу служебных данных, не связанных с моделями - ну тут уж никуда.

  • @devchuli5155
    @devchuli5155 2 года назад +3

    Привет, Михаил! Очень классный доклад и go-clean-template на гитхабе. Интересно, что нет юнит тестов для repo и webapi, получается, вы их тестируете заодно со слоем usecase. Или в реальных проектах вы пишете тесты для каждого слоя? Спасибо )

    • @neprja
      @neprja 2 года назад +8

      В первую очередь хочется тестировать бизнес логику, которая находится в usecase. При тестах usecase мокаются зависимости (repo, webapi, microservice_name). И то тесты пишутся не на каждый usecase. Надо смотреть на ситуацию, есть ли смысл вообще тестировать. Если есть требование на покрытие, тут конечно без вариантов) Но в целом юнитесты пишутся исходя из здравого смысла. Всё остальное покрывают интеграционные тесты.

  • @yarbersheer8559
    @yarbersheer8559 2 года назад +2

    Спасибо.

  • @MichailMishutkin
    @MichailMishutkin Год назад +6

    я только разбираюсь в подходах к чистой архитектуре на Go и выражаю огромную благодарность за такое ёмкое и простое объяснение темы (несколько месяцев просто не мог вьехать при самостоятельной реализации). Но есть ряд вопросов: В примере вы указали последовательные инъекции из репозитория в юзкейс, а потом из юзкейса в контроллер, но на диаграммах указано, что юзкейс как отдаёт, так и принимает через интерфейс данные и в репу, и в контроллер. Я правильно понимаю, что из-за формата обратные инъекции не указаны? или нет? если нет, то как конкретно общается контроллер с репой через юзкейс сохраняя подход чистой архитектуры?

    • @hakooplayplay3212
      @hakooplayplay3212 10 месяцев назад +2

      21:15 в usecase инджектится repository а потом в controller инджектится usecase, так происходит инверсия зависимостей что нижний слой не зависит от верхних , нижний слой инджектится в верхний. После инджекта слои связываются и спокойно общаются друг с другом по цепочке. request-> controller-> usecase-> repo-> usecase-> controller-> response. То есть не нужно путать связанность кодовой базы и связи слоев в работающей программе.

    • @MichailMishutkin
      @MichailMishutkin 10 месяцев назад +3

      @@hakooplayplay3212 спасибо! очень хороший ответ. У меня всё встало на свои места год назад(когда вопрос задавал) опытным путём. Переменная(ые) результата вызова метода и являются обратной связью по цепочке. Вроде элементарно, но очевидно только на опыте.

  • @AlexeiCheban
    @AlexeiCheban 2 года назад +6

    Такс стоп, вы в докладе рассказали что должен быть модуль infrastucture где находяться repo и webapi. А в репе они у вас в usecase находяться.

    • @neprja
      @neprja 2 года назад +2

      Репа немного устарела. Руки никак не доходят обновить. То что в докладе - актуально.

    • @AlexeiCheban
      @AlexeiCheban 2 года назад +1

      @@neprja Благодарю за ответ

    • @MichailMishutkin
      @MichailMishutkin Год назад +1

      тот же вопрос был))) спасибо!!!

    • @CloudOblakoRain
      @CloudOblakoRain Год назад +1

      @@neprja а можете обновить реп пожалуйста?

  • @svyatoslavazizov8231
    @svyatoslavazizov8231 Год назад +3

    Хмм, а как в вашем случае (на 25:50 показано справа) предполагается писать одни и те же методы (`New()`, например) для разных юзкейсов (apple/banana) в одном пакете usecase? Всё-таки надо их делить (создавать подпапки apple/banana внутри usecase) или есть возможность разграничить scope файлов apple/banana, чтобы компилятор не ругался на одинаковые методы?
    Просто если делить юзкейсы по папкам, тогда инфраструктуру, кажется, нужно выносить в отдельный пакет вне usecase, т.к. её могут использовать несколько юзкейсов (в репозитории из примера инфра лежит внутри usecase)

    • @neprja
      @neprja Год назад +2

      Спасибо за замечания) На счет репозитория из примера, он немного устарел, туда лучше не смотрите. Идеи со слайдов актуальны. У меня пакет infrastructure находится на одном уровне с controller, entity и usecase. По поводу New(), в юзкейсах у меня 1 New: (func New() *UseCases) внутри которого все абстракции, а методы от него это уже самостоятельные юзкейсы. По поводу New в entity: если у сущности появляется конструктор, значит сущность достойна отдельного пакета. Я его обычно помещаю внутрь entity.

  • @VitalyVakhromeev
    @VitalyVakhromeev 2 года назад +2

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

    • @neprja
      @neprja 2 года назад +3

      Отталкиватся нужно от юзкейса, новая задача = новый юзкейс. Все остальное вокруг него. Один юзкейс может оперировать несколькими сущностями и несколькими репозиториями в зависимости от задачи. Один юзкейс могут вызвать разные контроллеры (обработчики), их может быть несколько, а может быть один.

    • @VitalyVakhromeev
      @VitalyVakhromeev 2 года назад +1

      @@neprja Спасибо.

    • @Aaaa-jn4bm
      @Aaaa-jn4bm 2 года назад +3

      @@neprja , можете пожалуйста скинуть какой-нибудь годный репозиторий с чистой архитектурой, где применяется практика с описанием интерфейсов в месте использования ? Очень понравился такой подход, но нигде не могу таких примеров найти.. Глянул в ваш - там вы импорт интерфейсов делаете. Не то чтобы я сам не в состоянии написать код с таким подходом, просто хотелось бы на что-нибудь такое сеньёровское глянуть, где такой подход практикуется :) Ещё, очень желательно, чтобы это было api

  • @vadimalekseev3621
    @vadimalekseev3621 2 года назад +2

    А есть пример проекта по этому принципу с 50к строк кода? У меня в слое юзкейсов уже 10к строк кода и с каждым днем их количество увеличивается, выделять код в функции не получается, потому что тогда функции будут принимать по 10 параметров

    • @Aaaa-jn4bm
      @Aaaa-jn4bm 2 года назад +1

      Ну так в функции с более чем тремя параметрами нужно передавать структуры.
      Для каждой функции с большим количеством параметров создаётся input структура, разве нет ?

    • @aidarlatypov7747
      @aidarlatypov7747 2 года назад +1

      Совсем по верхам - ваша проблема в протекании модели в слой юзкейса

    • @user-rs7ty4kg2z
      @user-rs7ty4kg2z 8 месяцев назад

      Может быть нужно сделать дополнительные слои? Тогда зависимости будут везде от центра/центров к внешним слоям домена. Между слоями ставьте интерфейсы, чтобы внутренний слой знал самый минимум о более внешнем, точнее, заказывал только нужный ему функционал, а внешние слои этот функционал обеспечивали. Тогда изменения внешних слоев не затронет внутренний.
      Если приходится передавать много параметров значит вы не обеспечили принцип low cohesion

  • @ilgizilgiz
    @ilgizilgiz Год назад +3

    На 2 скорости становится нормально)

  • @dmitryvakulenko7980
    @dmitryvakulenko7980 Год назад +6

    Немного не согласен. Во-первых, нет смысла отделять интерфейсом слой контроллеров от слоя UseCase, т.к. UseCase никогда не будет вызывать контроллер, а контроллер вполне имеет право быть в курсе про конкретный UseCase согласно той же диаграмме зависимостей. Ситуации, конечно, разные могут быть, но в принципе это не нужно. И во-вторых, entity могут использоваться по всему проекту по той же причине. DTO никто не отменял, но, это, скорее элемент абстракции, а не ограничения видимости.

    • @neprja
      @neprja Год назад

      Да, согласен)

  • @MamontSFFP
    @MamontSFFP 2 года назад +3

    Балдеж

  • @user-hc6zm6mg5m
    @user-hc6zm6mg5m Год назад +8

    Ну хоть кто-то снизошел до уровня имплементации и четких примеров вместо бесконечных абстракций

  • @yarbersheer8559
    @yarbersheer8559 2 года назад +5

    А могли бы разъяснить в чём отличия кроме названий слоёв между Clean и Hexagonal, Onion архитектурами?

    • @neprja
      @neprja 2 года назад +4

      Отличий особо нет. Все они об одном и том же.

    • @yarbersheer8559
      @yarbersheer8559 2 года назад +3

      @@neprja простите за глупые вопросы, я ещё даже не джун. У вас хорошая слоистость, поэтому пока не просто сразу въехать. Я верно понял, что ваш слой controller это слой port в гексагоналке?

    • @neprja
      @neprja 2 года назад +3

      @@yarbersheer8559 Да. Контроллер - это точка входа.

    • @yarbersheer8559
      @yarbersheer8559 2 года назад +3

      @@neprja спасибо) а могу ещё отнять немного вашего времени вопросом.
      Часто вижу, что параметры среды не выносятся в отдельный сервис синглтоном, при этом очевидно, что эта функция может иметь несколько итераций построения объекта среды, например default -> flags -> env -> external file (yaml, toml etc).
      Почему не принято выносить эту процедуру в отдельный entity? Или просто обычно этому нет нужды уделять отдельного внимания?

    • @neprja
      @neprja 2 года назад +3

      @@yarbersheer8559 В entity ему точно не место, entity - это сущность бизнес-логики. А параметры среды - это конфигурация для построения объектов архитектуры приложения. Лучшая практика для переменных - делать синглтоном.

  • @user-zu5wr4su1q
    @user-zu5wr4su1q 7 месяцев назад +1

    красава

  • @kinvain
    @kinvain Год назад +4

    Совсем не понял про пустые import. Если usecase, к примеру, возвращает структуру entity то без импорта никак не обойтись. Разве нет?

  • @RusRes
    @RusRes Год назад +3

    Не совсем понял про "у меня use case ни в коем случае не вызывают друг друга". В моем мире это попросту невозможно. Сплошь и рядом новая бизнес-функциональность - это комбинация старой. Типа было яблоко, были креветки, и тут вдруг раз - и появился салат с яблоком и креветками. Как вы в этом случае будете поступать, все свои модули двигать между слоями и папками?
    Или обратный пример - всё начинается сразу с салата из 50 ингредиентов. Это будет один огромный use case внутри одной папки? А когда потом его части в другой папке понадобятся, как эти части повторно переиспользовать, копированием?
    Я не критикую, скорее всего это я что-то не так понял. Скорее пытаюсь понять, в чём тут идея. Потому что у меня как раз use case друг друга сплошь и рядом используют, но только через DI, без сильных связей

    • @neprja
      @neprja Год назад +3

      Давайте от обратного пойдем. Я не скажу как надо, но скажу как не надо. Если у вас один юзкейс вызывает другой юзкейс, то изменение в первом юзкейсе, повлекут изменения во втором. А это вам точно не нужно. Если у вас в одном юзкейсе используется другие юзкейсы, то вы обречены на страдания при минимальных изменениях. Принцип DRY можно забыть. Мартин говорит, что можете расслабится по поводу того что вам кажется что вы просто копируете код. И 2 юзкейса на начальном этапе могут выглядеть абсолютно одинаково. Если это разные юзкейсы их дороги в будущем разойдутся с вероятностью 100%.

    • @neprja
      @neprja Год назад +3

      Креветки и яблоки - это entity или абстракции (infrastructure), а в юзкейсах вы их отдаете либо не изменными, либо готовите из них разные салаты.

    • @RusRes
      @RusRes Год назад +3

      @@neprja Я наверное понял. Многое из того, что я для себя считаю юзкейсами, правильнее было бы считать частью доменной модели. Собственно, наверное по этой линии и проходит граница - домен переиспользуется, юзкейс уникален. Спасибо!

    • @neprja
      @neprja Год назад +2

      @@RusRes Верно

  • @user-zn9zo9mv2j
    @user-zn9zo9mv2j 10 месяцев назад +9

    Зачем мигать только не понятно. Хочешь слушать человека и одновременно смотреть картинку. Зачем постоянно переключать картинка-человек-картинка, человек объясняет сложный технический вопрос. Рябит в глазах и раздражает, переделать можете по нормальному видео? Чтобы человек без морганий справа, картинка слева, как это делается у нормальных людей. Вроде кодеры, уж это то могли "встроить" в видео, это элементарное юзабилити, чтобы зритель досмотрел доконца, вы же хотите чтобы вас выслушали, верно?

  • @konstantin6649
    @konstantin6649 2 года назад +3

    интерфейсы нужно размещать там, где используются - об этом не нужно думать :)

    • @neprja
      @neprja 2 года назад +1

      По дефолту, да.

    • @user-rz6xv8zt8n
      @user-rz6xv8zt8n 2 года назад +2

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

    • @neprja
      @neprja 2 года назад +2

      @@user-rz6xv8zt8n Хороший вопрос.
      Решение простое - вынести все интерфейсы в отдельный пакет.
      Раньше я так и делал. Но пришел к тому что структура юзкейсов у меня всегда одна и в неё заинжекчены все адаптеры. А методы этой структуры - это уже конкретные юзкейсы.
      И интерфейсы я кладу как раз рядом со структурой Usecases в пакет usecase. Получается что интерфейсы находятся в месте использования. Все, кроме 1 интерфейса самого юзкейса (для вызовах в контроллере). Можно было бы держать интерфейс юзкейса в самом контроллере, но контроллеров может быть несколько. Поэтому я нарушаю это правило (держать интерфейс в месте использования) только в этом одном случае.

    • @Aaaa-jn4bm
      @Aaaa-jn4bm 2 года назад +2

      @@user-rz6xv8zt8n , а разве это такая большая проблема ? Меня это вполне устраивает

  • @user-zg8ij3kt1h
    @user-zg8ij3kt1h Год назад +3

    Хорошо, что человек решил помочь разобраться в теме. Но изложение путанное и местами сумбурно.

  • @user-li7vj3sn9c
    @user-li7vj3sn9c Год назад +3

    Доклад классный, а вот того кто делал монтаж - пороть розгами. Показывают код, он переключает на докладчика. Homo Idiotus.

  • @silentium_noxe
    @silentium_noxe 8 месяцев назад +2

    оч много воды. По факту можно все уложить в 5 минут.
    1. Есть слои абстракций, они не пересекаются между собой напрямую
    2. Используй интерфейс для связи лееров
    3. Вот такая структура директорий
    4. Вот такой нейминг

  • @user-kt2mx5px3l
    @user-kt2mx5px3l 3 месяца назад +1

    Оратора как-будто пчела в шею укусила. Что это? Красные пятна и волдырик как-будто. Всю дорогу уже не мог нормально слушать о чистой архитектуре, отвлекаясь на эти непонятки. К середине видео краснота прошла, и норм стало слушать.

    • @neprja
      @neprja 3 месяца назад

      От волнения)

    • @errmaker
      @errmaker Месяц назад

      да уж )) неспортивный пацан какойто