0: В этом видео поговорим о архитектуре приложения и, в частности, архитектуре приложения на go. Научимся строить приложение с нуля. Поймём, какими принципами стоит руководствоваться при принятии тех или иных решений и.
1: И покажу эволюцию подходов в коде, ну и прорекламирую свой курс по go i go стеку.
2: Значит, что нужно делать для того, чтобы код оставался поддерживаемый? Ну, во первых, нужно уметь разделять вспомогательный и основной код. Об этом мы будем говорить большую часть сегодняшней лекции, когда мы определимся, что
3: Такой основной код. Будем учиться держать его в 1 месте компактно, но без потери информации научимся делить код на части и поймём, как это делать правильно и в целом, когда мы говорим про архитектуру,
4: Главную цель, которую мы преследуем, это упростить нашу жизнь, сделать код проще, доступнее для читателя, проще в поддержке, ну, чтобы, когда новый человек пришёл к нам, он быстрее онбордил я главная идея этого ролика как?
5: Отличать основной код от вспомогательного и вообще, что такое основной код для этого? Давайте сперва посмотрим, что такое вспомогательный код. Это большой кусок, связанный с инициализацией приложения. То есть у нас есть какой-то
6: И вот для того, чтобы привести приложение в состояние готовности, есть очень много разных инициализаторов, это инициализаторы, там соединения с базами данных и вообще
7: Все, что требует коннекшена там, kafka redis и также, видите, нам надо и логгер проинициализировать правильными настройками, и трейсинг там указать нужно метрики настроить дальше, какой ещё бывает вспомогательный код.
8: Server мы поднимаем, он ждёт, принимает запросы по арписи, мы можем принять запрос из кафка топика, читать месседжи, обрабатывать их, сделать какую-нибудь кронджобу, это тоже относится к этой группе вспомогательного кода.
9: Следующая группа вспомогательного кода это взаимодействие с внешним миром, базы данных, брокеры сообщений, редис, какой-то другой микросервис, который мы вызываем по клиенту. Также у нас есть какие-то хелперы и прочие
10: Код, который мы переиспользуем. Ну, библиотеки, понятное дело, мидлвары, интерсепторы, код, который отвечает за грейс шатдаун. То есть, это все не относится к основному коду. Это код вспомогательный. Что же тогда такое основной код это
11: Суть того, что делает ваше приложение, и оно обычно умещается на 1 экран, даже если оно делает что-то сложное, ну если прям супер сложное, много какие-то циклы, ветвления, 2 экрана, да, то есть вот условно смотрите, в чем идея со временем все усложняется, то есть
12: Становится все больше и больше абстракций становится больше. Хотелось бы, чтобы, тем не менее программы оставались простыми. И вот основная идея, чего мы хотим добиться, мы хотим из большого сложного
13: Приложение все-таки вычленить основной код, который определяет, что делает это приложение. И как раз в этой лекции мы будем учиться определять, что должно быть в основном коде, а что является
14: Вспомогательным, если кратко вся суть, вся идея архитектуры приложения, она заключается в этом. Так что же такое основной код? Ну, валидация данных? Я знаю, что многие из вас делают валидацию данных в хендлерах, в контроллерах, валидация данных.
15: На самом деле относится к основному коду. Работа с доменными моделями и функциями тоже относится к основному коду, не домен относится к основному коду, а работа с доменными функциями, методами относится к основному коду, а домен вообще, конечно, это отдельный пакет.
16: Тоже про это отдельно поговорим. Взаимодействие с сетью, да, взаимодействие с базой, с кафкой, с редисом, с другим сервисом. То есть все, что наш сервис делает по сети, должно отражаться в основном коде. Оно не должно заметаться куда-то под коврик мидлвары.
17: Да, оно должно быть видно сразу. Транзакции с базами данных исключения, конечно, всегда есть исключения, но мы говорим все-таки в этом ролике о том, к чему надо стремиться. И как все-таки чаще всего бывают циклы, ветвления, какая-то работа с горутинами тоже
18: Должна отражаться в основном коде в glow. Мы работаем с серверами, и про сервер надо думать, что это не 1 программа, а что это группа программ. Если у нас есть 2 ручки, то
19: Это 2 программы. Каждая ручка это отдельная программа. У каждой ручки, соответственно, есть свой основной код, например, ручка Гетт профайл. Сегодня будем говорить про домен профайл. То есть у нас какой-то сервис, который работает
20: Профилями пользователей. Вот у нас основной код, который валидирует данные и идёт в по, и это вот у нас вспомогательный код, а это у нас основной код. То есть в основном коде мы вызываем вспомогательный код. То есть смотрите,
21: Ну, тут, конечно, в голову обработка ошибок немножко, да, портит восприятие, но в целом это 1 строчка. Вот 1 строчкой мы валидируем библиотека юю айди и 1 строчкой мы идём в базу данных, получаем эти данные.
22: И отдаём их тому, кто нас вызвал. Мы не знаем, кто нас вызвал. В этом. И идея, что мы абстрагируемся от вызывающего. Смотрите, у нас есть данные на вход, есть данные на выход. Это дто. Я предпочитаю называть их
23: Их импута тут дто это data transfer object ну то есть это просто вспомогательные структуры, по которым видно наши входные данные для основного кода и те данные которые мы отдаём то есть дто это так?
24: Контракт нашего основного кода и просто структурами дто удобнее и однообразнее. Следующая программа. Следующий основной код это ручка крейт профайл. Смотрите, что мы делаем, когда хотим создать профиль. Мы
25: Мы хотим проверить дубликат в redis, мы хотим провалидировать данные и создать доменную структуру, дальше мы сохраняем профиль в постгрес и в кафку отправляем месседж о том, что мы профиль создали, так это будет выглядеть.
26: На схеме ещё раз фиксируем, что это код вспомогательный. И все эти операции тоже. Видите, они 1 строкой делаются. Мы в редисе проверяем ключи импотентности, мы создаём доменную структуру, ну и
27: Там же делаем валидацию, мы создаём профиль, посгресе и делаем кафка, продьюс, отправляем месседж. Итак, основной код должен быть кратким, то есть вмещаться на 1 экран он должен
28: Быть полным, то есть не скрывать важные этапы. Мы показываем, что глобально происходит наш основной код. Это и есть наша программа, мы не перегружаем основной код какими-то
29: Деталями реализации. То есть видите, мы одновременно и хотим понимать, что глобально происходит, но при этом мы не хотим какие-то детали, но мы не хотим скрыть какие-то важные детали допустим сетевой поход это важная деталь, не надо её скрывать, она должна быть
30: Code отражена и тем не менее мы не хотим упустить что-то важное. Например валидацию. Мы валидацию тоже делаем в основном коде. Наш основной код не должен быть частью другого основного кода, кто вызывает наш основной код.
31: Хттп или джер писи, сервер кафка, консюмер крон джоба Сивай команда. И основной код не знает, кто его вызовет. Смотрите, например, хттп сервер. Он тут получает данные запроса.
32: Декодирует их в нашу дто структуру. Дальше мы эту дто структуру передаём в наш основной код. Вот мы его вызываем. Мы получаем результат и отдаём его в джейсоне. Другой пример кафка.
33: Мы получаем из топика сообщение. Дальше мы вызываем наш основной код и делаем коммит сообщения. Если все прошло успешно, у меня тут, видите немножечко упрощённо, но суть
34: Вы поняли и ещё 1 пример это jersey server. Видите, мы тоже вызываем наш основной код, а дальше уже здесь работа происходит так, как это принято в джерис хендлере. Итак, вот наш основной код может вызвать хттп.
35: Jeris сервер может вызывать наш основной код. Да, консьюмер какой-то воркер. Основной код не знает, кто его может вызвать. Его может вызвать кто угодно в тесте мы его можем вызвать также без проблем. Ну, вы поняли. И отсюда следует, что артефакты вызывающей стороны
36: Они не должны просачиваться в наш основной код, то есть это какие-то контексты тпшные там джиновская протобаф, там какие-то структуры, месседж кафка, то есть message библиотеки кафки тоже не должен просачиваться.
37: Именно для того, чтобы он оставался независим от вызывающей стороны. И давайте введём уже термины. Вызывающая сторона называется контроллер. Мы эту группу вспомогательного кода будем называть контроллеры.
38: Хорошо, с вызывающей стороной зафиксировали. Поговорим о другой стороне, о том коде, который вызывает наш основной код. Мы можем вызвать редис, посгрес, кафку, другой сервис и так далее. И эту
39: Группу вспомогательного кода принято называть адаптерами в 99 процентах случаев. Адаптер это сеть. Если вдруг вы в вашем приложении хотите сходить куда-то по сети, не надо делать это в мидлваре или
40: Это ещё делайте, это в основном коде и нужен термин для нашего основного кода. Мы будем называть его юскейс. То есть если говорить про папки, в которых мы будем размещать код, то папка будет называться юскейс.
41: На мой взгляд, это наиболее удачное название для основного кода, как ещё называют основной код в разных кодовых базах, разных приложениях, разных языках, по разному принято сути это не меняет. Это одно и то же. Но название разное. Значит, смотрите, также называют сервис кор ворк.
42: Сценарий, процесс, логика, правила, бизнес логика, что-то такое. В общем, названий много. Суть 1. Мы будем называть юскейс. Контроллеры также называют хендлерами, ну типа обработчиками ро.
43: Api entry поинтами, или там энтрис как-то так, транспортом, эндпоинтами, гейтвеями, деливери в общем, сути не меняет вы поняли, по сути, про контроллеры надо думать как о точках входа.
44: Да, в приложение это то место, где все начинается. Когда у вас не сервер, то это просто, да, то есть, ну, начинается все в main файле, и все, допустим, у вас какая-то туза, все просто. А вот если у вас сервер, тут, конечно, надо думать о каждой ручке как о самостоятельной
45: Программе и просто сервер это группа программ. Адаптеры называют также клиентами, драйверами, гейтвеями, имплементациями или импл репозиторий персистенс. Вот это отдельная история про
46: Репозиторий и персистенс. Это обычно называют базы данных. Ну, не только тут, на самом деле вообще че угодно сюда попадает. Про это мы попозже тоже поговорим. И на этом слайде мы фиксируем направление зависимости. Смотрите, контроллер знает тольк.
47: Про юскейс контроллер в адаптер не входит, это понятно. Юскейс ничего не знает про контроллер, да, то есть кто его там будет вызывать, неизвестно. Поэтому мы и делаем здесь дто для того, чтобы побольше абстрагироваться.
48: Вызывающей стороны адаптеры у нас ничего не знают о юзкейсе. Они живут сами по себе, по сути, тоже самостоятельные программки. И это наши кирпичики строительные, из которых состоит основной код юзкейсы.
49: Логика, поэтому чем качественнее будет кирпичик, чем он будет универсальнее, тем в большем количестве подпрограмм. Мы сможем эти кирпичики использовать. То есть они у нас общие, они шаренные.
50: А вот это нет, это у нас уникальное и не переиспользуемые про обучение. Если вы начинающий гоу разработчик, или вы разработчик на другом языке, или фронтендер, если вы недовольны вашим текущим стеком, или может
51: Быть зарплатой недовольны и хотели бы перейти на go и пройти нормальное настоящее обучение. У меня для вас хорошие новости. 5 августа стартует 2 поток обучения по языку go i go стеку мы изучим гоу от основ до
52: Самого продвинутого уровня. Если вас это заинтересовало, то подписывайтесь на telegram канал. Ссылка в описании есть у нас ещё пэкэджи библиотеки, это тоже относится к вспомогательному коду и, ну они просто общие да, где хотим, там и
53: Пользуем. Ну вот посмотрим, что мы помещаем в pkg. Клиенты сюда попали. Это клиенты нашего собственного сервиса. Вот наш сервис можно вызвать по джерис, по хттп, то есть мы являемся джерис и хттп сервером, и нас можно
54: Вызвать и соответственно мы просто помещаем в pkg, а pkg это шаренные пакеты, которые могут использовать другие сервисы http сервер это код инициализации хдп сервера, инициализация логгера, инициализация метрик.
55: Инициализация посгри, редис рендер джейсона рендер ошибки это инициализация роутера. Ну типа мы создаём роутер, там же мидлвара по отлову пани чеки, как бы смысл каждый раз их писать тут можно.
56: Ообщить их и переиспользовать. И также тут у меня код, который работает с транзакциями посгри в pkg, у нас попадает код, который не зависит от доменной области. Ну понятно, потому что если зависит, то он находится в internal, а не в pkg. Вот верхний уровень. Вот смотрите как
57: Выглядит конфиг на верхнем уровне, чтобы сразу можно было легко найти и вот разделение собственно у нас архитектурные слои, они находятся здесь в интернале и видите, здесь в интернале нету пкг, потому что pkg это что-то близкое к утилс, хелпер что-то ругательное, поэтому он у нас 1.
58: На верхнем уровне туда помещается что-то то что шарится, что можно переиспользовать, то что можно обобщить, а код из internal не может использоваться другими сервисами это зашито на уровне компилятора специально так придумали, чтобы в интернале храни.
59: Архитектурные свои. Ну и вот они, собственно, да, тут мы храним адаптеры, контроллеры, дто, наш основной код, доменные сущности. Поговорим про домен. В основном с доменом работает основной код, контроллер может получить
60: Результат структуры домена, ну то есть там дто, а в дто доменная структура, адаптер, он, собственно, возвращает результат работы в структуре домена. Итак, домен это специфичные для предметной области структуры и функции.
61: Разные ручки могут использовать эту логику, которая есть в домене. То есть домен это шаренный слой и переиспользуемый код. Вот смотрите, ошибки помещаются доменные модели профиля.
62: Юниттесты, ещё какие-то модели и методы, и функции, связанные с предметной областью. Ну вот посмотрим, да, что у нас тут в домене конструктор создания этой модели и валидация, ну конструктор для того, чтобы нельзя было
63: Забыть валидацию. Здесь же, видите, у нас есть страк теги для валидации генерации джейсона. Они тоже живут, здесь же использовать их будут контроллеры, ну или кто угодно. Адаптеры тоже могут. В общем, это удобно, это в гошке допускается.
64: Тест, да, вот только что смотрели мы создание нового профиля. Вот это тест на него юнит тест. Здесь же все, что находится в пакете домена, должно быть 100% покрыто юнит тестами. Ну вот видите, ошибки, которые могут использовать все юскейсы, здесь может быть много
65: Ошибок, как ещё называют домен, можете встретить энтити по чистой архитектуре, модель от model view controller некогда популярной объекты да, агрегаты из дидиди. Кор бывает, встречается, но мне больше всего нравится домен термин.
66: Пришедший нам из ddd ddd domain driven design постоянно у всех на слуху постоянно выходят какие-то статьи об этом, хотя там книги были написаны в двухтысячных, в общем, интересные идеи, но слишком сложно.
67: Народ это не осиливает. Не могут программисты ни о чем договориться. В общем, вот эта тема, которая постоянно всплывает на всех конференциях, они делают доклады, но по мне так она приносит больше вреда, чем пользы, потому что
68: Каждый видит реализацию по своему, но тем не менее термин домен прижился, и он как бы мне нравится, потому что он хорошо отражает суть то, что находится в этом слое, то, что лежит в этой Папке.
69: Мы не будем говорить о ddd подробно, но некоторые концепции все-таки хотелось бы озвучить ну, во первых, единый язык да, хорошая идея, как называется в бизнесе, что-то так оно и должно называться в коде.
70: Это помогает всем говорить на 1 языке. Другая хорошая идея, баундед, контекст, ограниченный контекст. В общем, смотрите, в чем идея. Хорошо, когда у нас содержится в структуре только то, что нужно нашему сервису или конкретно
71: Нашему скоупу даже в 1 приложении может быть несколько структур ордер, просто они будут разные, зависит от контекста, что будет содержаться в этой структуре, правильно делать разные структуры с разным набором полей.
72: И методов просто главное обеспечить вот эту конвертацию, да, из 1 скоупа в другой. Почему это хорошо? Потому что универсальная хуже, чем узкоспециализированная. А хорошо, когда вы смотрите на структуру и видите в ней только то,
73: Что конкретно вам нужно, при этом вы в базе не храните то, что вам не нужно когда к вам приходит какая-то структура, вы не путаетесь что вам нужно, что не нужно, ну в общем, так как это ddd, то как вы понимаете, в реальной жизни тут возникает много вопросов, как это реализовывать.
74: Но тем не менее, просто на подумать, идея неплохая, такие темы в ddd как агрегаты ввели обжекты, это уже совсем все сложно, поэтому про это даже тут говорить не будем, есть ещё такая тема, как анимичные объекты и богатые.
75: Ну, это имеется ввиду, когда у нас анемичный объект, это у нас есть 1 структура, 1 модель и нет больше ничего, нет конструкторов. И чаще всего вы это и будете видеть в гошном приложении. Это просто модель с публичными полями.
76: Которую мы в общем то между слоями передаём. Вот это анемичная модель называется богатая модель. Это когда у нас есть конструктор и мы создаём нашу структуру через конструктор и при этом мы не можем забыть валидацию мы соблюдаем
77: Варианты этой структуры и это хорошо, и при этом методы делаются приватными. Я здесь так не сделал, потому что я использую подходы где-то посередине. Если здесь делать все приватным, то
78: Работать с этим будет неудобно и в гошных сервисах так не делают, но тем не менее круто иметь конструктор, который будет создавать этот объект, хоть и анемичный. И да, конечно, любой может к нему получить доступ, но тем не менее конструктор позволяет получить больше гарантий.
79: Для соблюдения инвариантов. Также мне нравится использовать строгую типизацию для таких полей, как стринги и Инты. Также можно к ним добавить свои собственные методы валидации при этом возникают. Видите, вот эти приведения?
80: Типов, которые не всем нравятся. Доменные области бывают большие и развесистые, и там может появляться много функций, много какой-то работы, какие-то преобразования и так далее. Хорошо, когда вы
81: Дробите вот эту работу в домене на мелкие части. Это позволяет переиспользовать код и повышает комбинаторику для того, чтобы основной код мог комфортнее работать с доменными.
82: Функциями и к тому же вы добавляете больше информации о том, что происходит для читающего код. Вообще важно в юзкейсе рассказывать историю, что происходит. И вот это позволяет дробление доменных функций на более мелкие
83: Позволяет рассказать эту историю качественнее, например. Ну, смотрите, вот плохо, когда мы создали нашу доменную структуру и просто вызвали какой-то 1 метод, а он там делает много всего. Ну, то есть там не 1 экран, да, там 3 экрана, 4 экрана лучше.
84: Бить вот то, что там делается на более мелкие части и по шагам показать, что происходит. То есть вот этот код он будет в юзкейсе. В основной логике. Такой код лучше рассказывает историю назвать функции, правильно назвать методы, правильно?
85: Так, дальше идём. Контроллер и основной код это и есть наша программа. И в сервере каждая ручка это уникальная программа. Основной код нельзя переиспользовать. Почему сейчас об этом поговорим?
86: Пример, вот наша ручка, нам дали задачу её доработать. Мы видим, что есть какая-то другая ручка, которая делает вот прям то, что нам нужно вот здесь вот именно код, который нам нужен, мы видим, что здорово он нам подходит.
87: Он работает с кафкой, что-то ещё делает, а нам именно это и нужно сделать. И мы такие берём и добавляем в наш основной код чужой основной код. Это грубейшая ошибка. Так делать ни в коем случае нельзя. Теперь изменение 1 ручки.
88: Затрагивают обе, а это разные ручки, это разные программы, у них разные жизненные циклы, разные пути, они разойдутся рано или поздно. Ну, особенно меня прикалывает, когда тут говорят. Так вот, этот сервис редко меняется туда сюда. Архитектура нужна для того, чтобы, если вдруг сервис
89: Начал активно меняться, мы смогли спокойно развивать сервис, потому что, по сути, мы никогда не знаем, вот сервис, он дальше будет развиваться, или он умрёт, или в него годами не будет залазить, когда вы начинаете какой-то проект и
90: И вы начинаете, не знаю, там у вас 1 сервис, 2 сервиса, 3 сервиса. Всегда какой-то из них начинает толстеть, какой сервис начнёт толстеть, вы не знаете, поэтому архитектура, она нужна, чтобы заранее подложить соломинку, чтобы мы могли
91: В случае чего, не страдая, делать изменения в коде, небольшое отступление, да, получилось так делать нельзя. Просто берете и вызываете вот этот вот адаптер, который используется в этом коде, который вам нужно.
92: То есть вы берете и просто вот этот код ещё раз напишите вот здесь у себя, который будет вызывать кафку продюсера и делать что-то ещё, потому что адаптеры у нас переиспользуемые. Понимаете, в чем идея? Вот этот код не переиспользуемый, а этот код переиспользуемый.
93: И поэтому нужно и стремиться к тому, чтобы вот там вот были какие-то мелкие функции, которые делали что-то 1, так другой разрез. Тут у нас уже вертикальный слайсинг пошёл, что у нас каждая ручка самостоятельная.
94: Программа, шарятся у нас доменные функции, методы, структуры, адаптеры, какие-то пакеты. И там тоже, кстати, важно писать функции поменьше для того, чтобы они удобнее могли переиспользоваться в разных кейсах. Это пере.
95: Используется, это не переиспользуется. Вот ещё интересный вариант извращенства. Значит, антипаттерн называется сервис хэл, как размывают основной код. Ну, в общем, это когда у нас сервис вызывать, сервис вызывать, сервис вызывает
96: Сервис, я думаю, вы все с этим сталкивались. А сервис, кстати, тоже это термин из дидиди. Ну, в общем, вот это тоже 1 из примеров, как люди неправильно понимают какие-то идеи и потом от этого все страдают. Значит, смотрите, вот у нас есть код, который
97: Вызывает редис, посгресс и другой сервис, а этот код вызывает кафку и ещё другой код. А этот код вызывает погрес, ещё какой-то код, этот код вызывает редис, ещё какой-то код. И вы вот как болванчик туда сюда прыгаете по этим функциям, а там чаще всего 2
98: Строчки кода, 2 строчки кода, 2 строчки кода, 2 строчки кода. И ты думаешь, почему бы вам было не написать все эти строчки кода просто вот здесь для того, чтобы не страдать. Вот как правильно это делать, мы не дробим основной код на части и
99: На уровне. Вот у нас он всегда в 1 месте, на 1 уровне, а на этом уровне уже шаренные адаптеры, ну или там домен пкг, что-то ещё. Наш основной код это просто скрипт. Основная программа разделочная доска.
100: Это наш кухонный стол, на котором мы готовим, а это наши ингредиенты. Ещё 1 важная деталь, о которой мы не говорили. Основная логика, она не работает с адаптером напрямую. Мы это делаем через интерфейс. Ну, из
101: Начальная идея для того, чтобы отвязать зависимость, чтобы в основном коде не было импортов вот этих адаптеров, а чтобы только лежали интерфейсы. Но в целом это, по сути, контракт. И он также помогает тестировать наш основной код и
102: Упрощает ментальную модель нашего основного кода, поэтому это обязательное требование к нашим адаптерам нужно обращаться через интерфейс. Таким образом, основная логика получается независимой и переносимой, что основная логика знает об адаптере, она
103: Знает только его интерфейсы. У нас есть интерфейс редис, который делает конкретную вещь. Есть кафка, который тоже что-то делает, есть адаптер с посгресом делает конкретные вещи. Их здесь может быть очень много, тут ещё
104: Как бы есть загон такой, что типа, а много методов в интерфейсе, это плохо, господа, это интерфейс другой. Это не Рейдер и не райтер интерфейс, который участвует всем
105: Языка это интерфейс, который служит для отделения адаптера от основ логики. Его предназначение только в этом. Ну и тут поэтому нет смысла его как-то называть, заканчивается там на your, потому что это интерфейс.
106: Именно архитектурный. Это часть архитектурного паттерна. Требование к адаптерам самое низкое, лишь бы он выполнял свой контракт. Там можно говнокодить, отдавать джунам. Проблемы очень легко локализируются. Если у вас есть проблемы с адаптером, вы это
107: Быстро заметите самое главное, чего не делает адаптер, он не делает ничего лишнего, он не должен размывать основной код. То есть он должен делать что-то атомарное, что-то достать, сохранить, обновить, удалить, проверить.
108: Править за продюсить. Ну давайте посмотрим хоть на 1 адаптер, что он тут делает код, который здесь будет находиться. Он не претендует на идеальный идеальный код должен быть в основном коде. А здесь адаптер лишь бы как-то работал. Ну вот у нас
109: Интерфейсы этих адаптеров, и мы, соответственно, с ними работаем, да, они вот сюда заинжекчены. Ну что это значит? Мы, в общем то, просто в конструкторе, когда мы создаём наш юскейс, мы конкретные реализации пробрасываем, они
110: Реализует вот эти вот интерфейсы. Таким образом мы отвязываемся от конкретных реализаций и наш основной код ничего не знает, как они там внутри работают. Наш основной код работает с интерфейсами. Ну и это очень полезно.
111: Для тестирования если вдруг мы захотим это делать это все naprimer можно замокать отдельно про репозиторий и persistence популярное название адаптеров не называйте так адапторы в своих проектах потому что это уже.
112: Паттерн, откуда это пошло? Это неправильное трактование паттерна. Понимаете, абстракция это интерфейс, да, вот если вы используете посгрес по интерфейсу, вот это и есть
113: Абстракция. А то, что вы назовёте при этом посгрес, как-нибудь персистенс или репозиторий, это вы только затрудните код читающему, ему надо будет. Ну, то есть я когда вижу, что репозиторий, надо посмотреть, что там внутри. Ага.
114: Надо запомнить, потом в другом месте репозиторий будет другой лежать, и ты встречаешь, когда репозиторий, думаешь, так это постгрес или редис. И вот они там вот так вот чередуются и так далее. Короче, абстракция это интерфейс, а интерфейс может называться конкретно.
115: Ещё 1 интересный пример из этой же серии кэш. В общем, если у вас кэш ин мемори, можете назвать его кэш, но если у вас кэш редис, назовите его редис, это будет всем проще и удобнее. Сразу понятно, что за интерфейсом.
116: Вы просто усложняете жизнь читающему код, если называете редис кэшом. Вместо редиса ещё 1 интересный паттерн декоратор. Ну вроде бы декоратор. В общем, требуется нам добавить кэш. Такая мода сейчас делать это через
117: Дополнительный слой с тем же самым интерфейсом. Вот допустим, есть посгря, да, у нас. И мы хотим добавить кэш к этой посгре. И вот с этим самым интерфейсом, с тем же самым добавляется ещё какой-то кэширующий слой. Ну неважно как он называется, пусть с утра здесь будет и соответственно, там у нас
118: У нас есть логика дополнительная, в зависимости от ручки. Мы что-то кэшируем, что-то не кэшируем. Ну смотрите, простота в том, что вы можете легко взять и вот только, допустим, заюзать, а можете взять, подключить редис 1 строкой и у вас как
119: Уже и через кэш работать. То есть типа вот в этом бенефит этого подхода. Но минус в том, что вы размываете логику, у вас часть кода отъезжает сюда, это становится сложнее поддерживать, легче сделать баг и об этом сложнее думать.
120: Лучше делать плоско весь основной код в 1 месте. А если уж вы там так часто хотите включать, отключать кэш, ну добавьте. If это будет все равно не во всех ручках поддерживать этот код будет проще. Здесь ещё можно забыть, что он там есть. Ну то есть вы читает.
121: Код, понимаете, что база вызывается, а что там ееще кэширующий слой с походом по сети? Нифига себе. Важная информация. Да, это выносится отдельно. Хорошо, плохо. Делайте всю логику в 1 месте. Ещё 1 антипаттерн, когда поход по сети
122: Делают мидлваре. Допустим, вы хотите проверить дубликат в редисе и помещаете его в мидлвару. Дело в том, что поход по сети это очень важная штука и хотелось бы видеть её в основном коде, поэтому не надо
123: Ходить в сеть в мидлваре, когда в сеть входит основной код, логика не размывается. В целом мидлварами не стоит увлекаться. Валидация данных тоже относится к основному коду. Это часть логики. Давайте такой вам пример покажу. Вы добавляете ещё 1 контроллер и можете забыть.
124: Что надо делать на него? Валидацию, или эта валидация будет другая, или потом эта валидация поменяется, вы поменяете её здесь, а в контроллере забудете поменять. То есть пришли вам какие-то данные извне, вы их просто превращаете.
125: В дто, а дальше уже основной код разберётся, что там валидно, что невалидно все проверит. Если плохо вернёт ошибку. Транзакции тоже находятся в основном коде. Важная часть логики. И удобно сделать, чтобы метод
126: Базы были универсальными, то есть они работали одновременно и с транзакциями, и без транзакций. И вам не нужно было думать, когда вы добавляете в основном коде транзакцию, поддерживает ли её адаптер, как я это делаю. Вот это пример.
127: Юзкейса, то есть основной логики используется транзакшн враппер, в который мы передаём функцию сигнатура. Видите, тут контекст возвращает ошибку и внутри мы делаем в транзакции 2 похода в базу, то есть создаём
128: В профиль и тут создаём какие-то свойства, как это устроено. Смотрите, вот функция врап, вот мы передаём туда нашу функцию с 2 вызовами адаптера вот она здесь вызывается но перед этим мы делаем
129: Транзакции сразу в бэк, если че то пошло не так и здесь коммитим. В общем, нужно учиться разделять основной код и вспомогательный. То есть видеть где вот у нас эта часть программы, а где это просто помощники, не
130: Размазывать основной код приложения, держать его в 1 месте и не переиспользовать основной код в другом основном коде из того, что в интернетах можно найти красивое. Ну, собственно говоря, здесь есть тоже самое движение.
131: Зависимостей, оно здесь слева у нас контроллеры, ну, здесь называется праймеры, адаптеры. Ну, на самом деле это архитектура, порты и адаптеры. И здесь порты это интерфейсы, а праймеры, адаптер это контроллеры. Поэтому вот левая часть это
132: Контроллеры, а правая часть это адаптеры ну и вот здесь какие контроллеры есть там admin gui хотя вот здесь, смотрите, контроллер контроллер controllers, да, как бы только Вон Сивай команда не контроллер, так-то можно было все-таки контрол.
133: Это назвать, да, вот, вот у нас как бы тут ядро приложения аппликейшн Леер, это, ну вот, собственно, юскейс наш, да, основная логика доменный слой, здесь домен Леер. И вот через интерфейсы. Ай, это тут
134: Интерфейс. И вот он идёт в адаптер, вот смски отсылает адаптер, вот там почту отсылает адаптер, че то ищет ормка. Ну, ремки не принято в го использовать, мы просто напрямую в базу ходим, ну, нигде высоконагруженных.
135: Проектах не использует. Вот. То есть в целом, в целом, как бы схемка прикольная. Вот говоря про архитектуру, нельзя не сказать про дядюшку боба, не всегда он умеет себя хорошо преподнести в интернете, но ему можно все простить, он сделал
136: Очень много для популяризации правильных идей. Что главное здесь хотел показать дядюшка боб. Смотрите, вот в чем идея. Контроллеры у нас вызывают юскейс, да, то есть, как бы юскейс, он ничего не знает о контроллерах, поэтому юскейс
137: Он независим от контроллеров. Юскейс вызывает адаптеры, но почему здесь стрелка в другую сторону? Потому что через интерфейс это делается. Понимаете? Здесь инвертируется таким образом, зависимость, в общем то, половина чистой архитектуры посвящён
138: Поэтому инверсия зависимости это про это, а юскейс он вызывает, соответственно, домен, и поэтому тут домен, ну вот энтити, это домен находится в центре, и он такой посмотрел на это, как это нарисовать. И вот нарисовал диаграммку круговую, вот на контроллере при
139: Запрос, значит, вот он так прошёл до юзкейса. Юскейс вызвал базу данных, вернулись результаты там энтити. И вот мы вернули запрос нашему серверу.
140: Вот это зелёненький, это интерфейс, типа, вот, вот эта идея, вот эта в зелёном отражена, что вот здесь вот это интерфейс так показан моя версия, в общем то, как я это рассказываю, вот у нас есть контроллер свой, да, он идёт
141: В основную логику в юскейс через дто. Основная логика там через интерфейс в базу может сходить в кафку, может сходить, при этом возвращаться будет доменная структура. И поэтому, как бы таким образом домен используется не только в основной логик.
142: Ну и видите, в адаптерах тоже, но и когда основная логика будет возвращать в дто, здесь тоже будет находиться доменная структура. И поэтому, как бы вот доменную доменный слой, он используется и в контроллерах, как бы, и
143: И в адаптерах, но по большому счёту, хозяин домена это основная логика, поэтому ближе всего домен все-таки к основной логике. Ну и поэтому здесь вот в кейсах, видите, вот типа он внутри находится, здесь то логика чисто про зависимости, то есть цель по
144: Показать была, что доменный слой ни от кого не зависит, что вот юскейсы, они зависят от этого доменного слоя. Вот. А доменный слой типа, он независимый, но гораздо важнее показать, что юскейс ни от кого не зависит. То есть
145: Он не зависит от контроллеров, потому что контроллеры его вызывают, и он не зависит от адаптеров, ну потому что через интерфейс и вот это была основная идея нарисовать пару слов про secures слишком много внимания уделяет.
146: Этому паттерну на самом деле паттерн простой, как Верёвка и палка. В чем суть тут у нас если ручка пишет, то это типа команта. Если ручка читает, то это типа квери. И вот идея в том, что читающие ручки, они будут в репликах читать, а пишут
147: Ручка будет мастер писать. Ну, в общем то это вот так вот просто реализуется 2 разные ручки. По факту паттерн очень простой. Пару слов про конфиги. Конфиги я храню на верхнем уровне для того, чтобы эксплуатация их быстро нашла, конфиги хранятся в своих
148: Пакетах, то есть мы не храним структуры конфигов. Все на 1 уровне, а конфиг логгера хранится в логгере. Логично, логично. И поэтому, когда вы начинаете новый сервис, вам не надо здесь прописывать все эти конфиги берете и из pkg использу
149: Эти готовые конфиги, ну вот, например, конфиг логгера, да, вот функция нит конфига, она просто принимает этот конфиг. Очень удобно. Пользуйтесь ямал или томал или переменные окружения. Лучше всегда использовать переменные окружения.
150: С разумными дефолтами. То есть вы тут дефолтные какие-то значения разумные указываете, чтобы, если что, и посмотреть их легко было. И понятно было какое-то значение легко залезть в кубернетис, посмотреть, какие значения легко их поменять. Ну, основная функция запуска приложения, да, то
151: Для того чтобы собрать все наши слои, все вместе. Вот мы тут инициализируем посгрес редис кафка, продюсер и дальше собираем структуру для нашего основного кода. То есть мы инжектим все адаптеры.
152: Кейс и дальше идёт запуск консьюмера и хттп сервера. Дальше ждём сигнала и при получении сигнала делаем грефен даун и давайте покажу на примерах эволюцию подходов к архитектуре на go. Вообще есть вот репозиторий.
153: Project лайаут все, кто начинает изучать go, так или иначе видели его, на самом деле здесь не так интересно, что тут за папки самое интересное это папка internal 1, что мы здесь видим, что здесь всего лишь 2 папки.
154: Предлагают эп и pkg и pkg с приватными либами, по сути не предлагают нам никакой архитектуры, кроме того, что мы просто ну там билдим как-то наше приложение и складываем в pkg, в плоскую структуру какие-то.
155: Пакеты, то есть архитектуры нет, и раз так, то большинство гошников такие репозитории и делают. Когда начинают новый проект, кто-то жалуется, когда переходит на go, что на go нельзя писать большие сложные проекты, потому что, ну вот такую плоскую структуру, если
156: Вы их будете заталкивать, то у вас лапша будет получаться очень быстро. Это первородная проблема go. И очень странно, что до сих пор здесь нет какого-то, какой-то вменяемой рекомендации.
157: Как делать так, чтобы проекты были поддерживаемыми? Я сделал публичный репозиторий, просто покажу вам вкратце обзорно какие-то эволюционные моменты. Ну и вообще современные подходы покажу. Так, сперва посмотрим, как это чаще всего вы
158: Будете видеть. То есть у нас есть публичные пэкеджи, приватные пэкеджи. И обычно мы видим сервер. Это здесь как раз ручки, ну, роуты и ручки сервера хранятся, и какие-то
159: Типа работаем с редисом, ну здесь а может быть и в pkg лежать с посгрисом, работаем, здесь тоже в pkg может лежать модел, как без этого, то есть какие-то модельки, ну вот такие просто примитивные, обычно даже без валидации, просто, просто структура.
160: И, ну вот это мы работаем с кафкой, да, значит, плохо, что обычно пишут логику в самом, в самой ручке сервера, и получается, что, ну, хорошо, если там как-то
161: Ещё бывает заинжекчены вот как у меня здесь да в henderson меня тут инжекшен, кафка и редис, а уже вот отдельно в ручке это у нас дтп ручка принимаем, запрос декодируем и здесь вот идёт бизнес.
162: Пошла, да, редис. Вот мы сделали структуру, провалидировали в базу сходили в кафку сходили. И вот опять отдали джейсон. И здесь основная логика основной код, это
163: Это вот это вот а вот это видите это контроллер и в общем то это я вам показывал что это нужно выносить отдельно get profile да, вот он тут более короткий просто ходим в базу парсим её id входим в базу вот казалось бы простой
164: Простая ручка. Че, типа, зачем усложнять? Можно все в рендере написать, но сейчас простая. И вы не знаете, как она будет усложняться дальше и неминуемо это все превратится в лапшу. В итоге так хорошо. Значит 1, что мы должны сделать, это вынести вот этот наш
165: Основной код отдельно. 2 тип сервисов. Я вот назвал сервис fast. Ну потому что юскейсы часто называют сервисами. Я все-таки люблю бизнес логику основной код называть юзкейсом, но сервисами тоже часто называют вот
166: Здесь я показал, например, с сервисом, значит, у нас есть профайл сервис, и у нас сервер уже вызывает этот самый профайл сервис, он у него тоже тут заинжекчены, вот он заинжекчены. И просто в контроллере мы вызываем этот основной
167: Наш код и получается отдельно. У нас контроллер живёт и отдельно вот сервис. Хорошо. Уже лучше. Ну вот ручка основного кода Валида.
168: Поход в базу для get профайла, но здесь можно свалиться в сервис хэл. Как это выглядит? Значит, вместо того, чтобы
169: Вот, например, здесь у нас сервис есть в редис, он ходит в постгрес и добавляется некий месседж сервис. То есть вместо того, чтобы напрямую в кафку сходить, мы сделали ещё 1 сервис, который
170: В кафку ходит и, например, ещё че-нибудь делает. Вот он там входит в кафку и делает что-то ещё. И вместо того, чтобы самим сходить вот это сделать, да, мы идём вот в этот месседж.
171: Сервис делаем, отправляем месседж, а уже там этот сервис отправляет в кафку. Вот это плохо. Так делать не надо, потому что у вас размазывается основной код, размазывается бизнес логика лучше.
172: Это делать в 1 месте. А, да, и ещё что плохо, что этот message сервис может переиспользоваться другими тоже сервисами и все. И вот код, который здесь, он становится зафрижен, потому что его любое, любое его изменение затрагивает кучу кучу.
173: Ручек куча кода. Проект превращается в лапшу, поэтому отсюда и правило держать весь основной код в 1 месте это уникальный код. Его нельзя переиспользовать в других подпрограммах, в других ручках. Следующий этап эволюции это уже
174: Слоистая архитектура, она же hexagonal архитектура, она же порты и адаптеры, она же луковичная архитектура, она же чистая архитектура вот, в общем то, это все про одно и то же, что у нас есть.
175: Свой с адаптерами и он шарится. У нас есть свой с контроллерами и контроллер плюс юскейс это самостоятельная программа. Контроллеры. Видите, у нас здесь вот есть кафка.
176: Который вызывает основной код. Есть хттп, который вот create профайл, вот он вызывает крейт профайл, я здесь назвал юзкейсом, а здесь ещё у меня сервис, ну, тоже надо было юскейс переименовать. Вот, короче, он вызывает
177: И уже вот, вот конкрет профайл вызывает. Мы попадаем сюда и вот наша основная логика, она принимает, видите, тут имя, возраст, имейл.
178: Возвращает её id. То есть здесь без тошек у меня сейчас сделано, потому что дтошки следующий этап эволюции. Вот появляется папка домен и складывается он, ошибки общие складываются домен. Ну здесь у нас так и остались, пока эти структуры, до них тоже дойдёт.
179: Сейчас эволюция у нас вот, и это уже сильно сильно лучше. Это уже более поддерживаемая вещь. У нас тут структура юзкейсов, вот она имеет адаптеры, а каждый метод от этой структуры это само
180: Юскейс, самостоятельная программа. Так, ну и здесь же сразу вопрос. Типа, если у нас становится очень много юскейсов, видите, структура юзкейса, она 1 всего основная, если их становится много. Ну, во первых, смотрите, обычно делают 1 микросервис, равно
181: 1 домен, если у вас появляется несколько доменов, допустим, 2, ну, они, в принципе, здесь ещё уживаются, если здесь становится там 15, 20 файлов, 15, 20 методов, конечно, это уже плохо. И в этом случае
182: Обычно в интернале просто делают, короче, делают 2 юзкейса разных, и получается, как бы мы на верхнем уровне оставляем только контроллеры, потому что они общие, а потом свои собственные доменные структуры юскейсы находятся в своих собственных папках, ну, типа,
183: Вот так вот делается.
184: Значит, 1 домен apple, 2 домен банана, и у нас у них свои собственные есть домены, есть кейсы, то есть и у apple есть свои собственные домены, и есть кейсы, и у банана есть свои собственные домен.
185: Есть кейсы. Вот. И получается, что контроллер, он все равно тут общий вызывает тот или другой домен. Ну, можно и контроллер на самом деле тоже внутрь туда затолкать. А у эппла есть свой домен, есть кейс, у банана есть свой домен, есть кейс. Вот, в принципе, тоже.
186: Рабочая тема, вполне все работает. Ещё бы хотелось рассказать о таком достижении человечества, как изобретение файлов и папок. Смотрите, вы можете не держать весь код в 1 файле, а то бывает такое, знаете, типа заходишь в пакет там
187: Просто 1 файл и все находится там здорово когда вы все-таки вот у вас отдельная программа крейт профайл, вот вы её отдельно держите, здесь ничего лишнего нет вот get profile тоже отдельная программа в 1 месте держите, это позволяет уменьшать скоуп, разгружать голову и вы ну
188: Просто используете вот это достижение человечества, да, когда вы можете не по коду вот так вот, как бы бегать, а слева щёлкать по файлам. Почему это взялось? Откуда вот это вот много, много кода в 1 файле, потому чт.
189: Люди смотрят, как пишутся библиотеки на go. И здесь вот 1 файл, да, и здесь он 700 строк кода. Ну, понимаете, это библиотека. В библиотеке просто навалены функции, да? Ну, там бывает, конечно, и кор.
190: И доменные тоже бывают штуки у библиотек, но обычно это просто набор функций. Вот у нас же более сложное что-то у роберта Мартина есть статья про кричащую архитектуру, там идея простая, что если вы заходите в проект, вы должны
191: По дереву папок и файлов понять, что происходит в этом приложении. И это очень классная мысль. Ну, заходите, да, в дерево, не видите кода, просто файлы, папки смотрите, и вы понимаете, что происходит.
192: И вы примерно понимаете, где, что находится, где, что искать. Это хороший маркер, что вы все делаете правильно. Следующая идея. Значит, дто, ну, смотрите, мы берём и добавляем дто в
193: Основной код, то есть как бы добавляем контракт в виде дто на основной код. Вот у нас есть case создания профайла, и мы стандартизируем ввод и вывод в этот профайл. И у нас все есть кейсы, так у всех есть свой
194: Дто входа своего этого выхода. Ну, минус, что они тут длинные, конечно, немножечко называются по длинному, потому что они лежат все в своём этом пакете. Дто можно в теории, в домене хранить, но там тоже длинные названия получается. То есть, ну, мы там
195: Посмотрим сейчас слайсов архитектуру, где это решён этот вопрос. Но несмотря на то, что тут это длинное, и приходится, например, вот тут вот так делать структуру. Ну, мы ни не возвращаем, мы не де.
196: Их указателями. Поэтому мы и вынуждены вот так делать, чтобы здесь не возвращать длинную вот эту структуру. Добавляется этот контракт. Видите, тут он можно внутри возвращать доменный какой-то объект. Это все окей. Вот.
197: Дто понятно, что можно делать и на адаптеры, но зачем адаптор может просто доменную модель вернуть и ничего страшного здесь нет, как бы именно хотелось бы этот контракт иметь на взаимодействие между контроллером и кейсом. Следующее.
198: Эволюция это ddd. То есть мы домен немножечко делаем не просто модели, видите, с, так сказать, велью обжектам, если про домен говорить, ну давайте без этого короче, просто тип
199: Построже. Не string здесь принимаем, а name это избавляет от некоторых ошибок. Плюс мы создаём нашу структуру в конструкторе. И как вариант, да, я говорил, можно здесь ещё сделать приватные поля, но я делаю публичные, я делаю такую
200: Гибридная модель, меня она устраивает, и мы чуть чуть повышаем надёжность создания наших структур. Мы точно не забудем валидацию. Ну, секьюар эс, давайте хорошо, тоже покажу. Тут очень простая идея, что вот
201: Есть у нас посгрес, мастер, есть посгрес, реплика и у нас отдельные вот create профайл, он работает с погром мастером, а get profile работает с погром, репликой, ну как бы в общем то на этом все, так. И последняя идея про
202: Вертикальный слайсинг. Ну смотрите, если мы вот даже на нашу архитектуру посмотрим, так вот, сверху, да, вот контроллер, вот create профайл, дто криейт профайл, эскейс крейт профайл. То есть у нас как бы кусочки кода раскиданы.
203: По разным папкам. Ну потому что разные слои, да, и
204: Почему бы нам не взять и не поместить их в 1 папку? Ну типа, мы же и так, когда эту структуру делаем, мы не хотим, чтобы они были связаны между собой. Это просто группа программ, которые живут вместе.
205: Но они оказываются в 1 слое, да, ну потому что они тут адапторы юзают вместе и так далее. Вот. Но это решаемо с адаптерами не так уж и сложно на каждый юскейс свой набор адаптеров прокинуть, почему бы не поместить их в 1 папку и
206: Вот у нас получается слайсов архитектура, значит, что у нас адаптеры общие, так и остаются адаптеры могут юзать любые программы. Это переиспользуемый код, контроллеры. Ну, я тут оставил только потому, что у меня тут роуты настроены, так-то они
207: Видите, разные программы вызываются под программы вызываются. Тут, кстати, удобнее получается, вот пакеты писать. То есть нейминг более красивее выходит. Вот. Ну, можно этот роутинг и куда-то в другое место убрать. Так, ну, самое интересное, где у нас живут юскейсы. Вот у нас
208: Profile папка и вот визуально мы видим что у нас есть 2 подпрограммы, 2 ручки соответственно 2 2 разных юзкейса и что у нас внутри вот у нас наш юскейс крейт профайл
209: У него тут зависимости, ну, те же самые интерфейсы, но просто у него только 1 метод create профайл, и все. Вызвать его могут. Либо хттпшный код может консьюмер вызвать. Вот у нас тут кафка, консьюмер тоже.
210: Тоже может его вызвать. Здесь у нас папка дто, можно нейминг упростить, потому что у нас пакет получился уже крейт профайл по фиче. Вот. И, соответственно, здесь тоже, видите, он инпут, аутпут. То есть тут нейминг упростился, ну и тоже самое.
211: Есть подпрограмма ручка Гетт профайл. Вот у неё основной код, здесь адапторы, с которыми она работает. Вот работает ручка с 1 адаптором, он тут 1 есть, будет ещё с 1 адаптором работать, мы добавим. То есть здесь ещё получается, мы ещё
212: Уменьшаем скоуп тем самым, а до этого подход был там просто все адаптеры были в доступе. Ну и вот то, что тоже тут дто. И вот контроллер, который вызывает этот юскейс, вот обзорно показал, если хотите
213: Посмотреть код ссылка будет в описании, и бонус для тех, кто дошёл до конца, покажу немного статистики про 1 поток я провёл опрос, и 9 человек согласились в этом опросе участвовать про качество обучения.
214: 2 трети очень довольны, треть скорее довольна. Почти всем было очень комфортно взаимодействовать с преподавателем. Ну, то есть со мной всегда все получали ответы и помощь. От меня почти все ответили.
215: То значительно продвинулись в профессиональном уровне после обучения никто не жалеет о потраченных деньгах, и какие темы запомнились больше всего. В общем, если хотите принять участие в обучении во 2 потоке, то ссылка
216: На telegram канал нахо.