9 лучших практик разработки микросервисов
источник https://t.me/itmozg/9693
источник https://t.me/itmozg/9693
Чтобы овладеть навыками создания масштабируемых приложений, вам необходимы курсы микросервисной архитектуры. Для тех, кто сомневается в перспективах этого направления, статистика говорит сама за себя: средняя зарплата для специалистов Junior+ составляет 250 000 рублей, для Middle+ — 350 000 рублей, а работники старшего звена могут рассчитывать на доход в 500 000 рублей.
После тщательного анализа мы определили лучшие обучающие программы по архитектуре микросервисов, которые могут вывести выпускников на профессиональный уровень.
Микросервисная архитектура от Otus.ru — лучший тренинг по освоению инструментов для разработки микросервисов.
Микросервисы: проектирование и интеграция на GO от Slurm — идеальные уроки по разделению монолита на микросервисы.
DevOps-инженер от Нетология — эффективное обучение развертыванию Kubernetes и автоматизированному управлению конфигурацией.
Python-разработчик от Eduson Academy — возможность освоить универсальный язык Python.
DevOps-инженер PRO от Skillbox.ru — уроки по эффективному сочетанию процессов разработки и эксплуатации.
ClickHouse для инженеров и архитекторов БД от Otus.ru — идеальные лекции по использованию ClickHouse в своей работе.
Microservices - паттерны от PurpleSchool — самое быстрое изучение универсальных шаблонов, полезных разработчикам на разных языках.
Enterprise Architect от Otus.ru — эффективное обучение анализу компании и улучшению ее архитектуры.
Полный путь от монолита на Rails к микросервисной архитектуре от Thinknetica — лучшие лекции по успешному внедрению архитектуры в производство.
Микросервисная архитектура от IBS Training Center — наиболее подробное изучение вопросов развертывания и методов обеспечения необходимых качеств архитектуры.
Этот набор тренингов предназначен для бизнес-аналитиков, системных администраторов, а также менеджеров проектов, продуктов и портфолио. Перечисленные обучающие программы дают необходимые навыки для принятия обоснованных решений, способствующих развитию вашей компании или направления. Погружение в главные аспекты микросервисов и их интеграцию позволит вам эффективно управлять проектами, анализировать процессы и успешно внедрять современные архитектурные подходы.
Программа предназначена для архитекторов, системных аналитиков, разработчиков и администраторов, стремящихся овладеть передовыми методами разработки микросервисных систем. Она включает в себя решение нетривиальных задач проектирования, направленных на создание масштабируемых и отказоустойчивых приложений. Вы узнаете, как использовать классические шаблоны проектирования, такие как сегментирование и репликация, а также освоите работу с Kubernetes.
Стоимость: от 14 637 рублей в месяц
Длительность: 5 месяцев
Формат обучения: онлайн-лекции, практические задания, итоговый проект
Сертификат: да
Преимущества:
возможность получать обратную связь;
создание фреймворка интернет-магазина как практический проект;
учебные модули с акцентом на реальный опыт;
знания экспертов.
Недостатки:
ограниченное время для практических занятий;
иногда преподаватели слишком спешат.
Программа обучения:
Базовые паттерны
Инфраструктура микросервисов
Инструменты наблюдаемости
Коммуникационные паттерны
Распределенные системы и хранилища
Проектная работа
Тренинг от Петра Щербакова, опытного архитектора решений, дает представление о разделении монолита на микросервисы. В течение месяца обучения вы измените свой образ мышления, освоите шаблоны и структуры микросервисной архитектуры. Эксперты из ведущих компаний Марсель Ибраев и Павел Селиванов поделятся с вами практическим опытом и глубоким пониманием эффективного проектирования и интеграции микросервисов.
Стоимость: от 45 000 рублей
Длительность: 36 часов
Формат обучения: вебинары, домашние и практические задания, тестирование
Сертификат: да
Преимущества:
спикер с 8-летним опытом коммерческой разработки;
обучение на реальных кейсах;
обратная связь от спикера;
доступ к материалам сохраняется в течение 2 лет;
большое количество практических заданий для глубокого понимания темы.
Недостатки:
некоторые уроки требуют больше практических примеров;
необходимо поработать над структурой учебной программы.
Программа обучения:
Анализ монолита, основные проблемы монолитов
Проектирование и стратегия разделения часть
Документирование
Управление изменениями и поддержка
Встреча со спикером
Рассмотрение механизмов интеграции
Реализация механизмов интеграции
3. DevOps-инженер | Нетология
Тренинг предназначен для тех, кто стремится стать высокооплачиваемым специалистом по DevOps. Под руководством опытных преподавателей Булата Замилова и Андрея Копылова вы освоите все ключевые инструменты для решения инфраструктурных задач и автоматизации процессов. Слушатели узнают, как администрировать кластер Kubernetes, работать с Ansible, настраивать удаленные серверы и использовать Terraform для настроек серверной инфраструктуры. Приобретенные навыки позволят вам организовывать проекты с использованием облачных провайдеров, настраивать процессы CI/CD и успешно применять подход DevOps в реальных проектах.
Стоимость: от 2916 рублей в месяц
Длительность: 7,5 месяцев
Формат обучения: вебинары, видеолекции, практические задания
Сертификат: диплом о переподготовке
Преимущества:
занятия проводятся три раза в неделю с возможностью записи;
прохождение дипломного семинара в облачном сервисе Yandex Cloud;
построение современного процесса DevOps;
создание портфолио;
поддержка командой экспертов, координаторов и менторов.
Недостатки:
возможны переносы занятий;
при подаче заявки на тренинг могут возникнуть небольшие трудности.
Программа обучения:
Инфраструктура как код, непрерывная разработка
Kubernetes
4. Python-разработчик | Eduson Academy
Уроки разработаны для тех, кто хочет стать востребованным специалистом в области создания веб-приложений, видеоигр, искусственного интеллекта. Изучив универсальный язык Python, вы сможете выбрать сферу деятельности, от тестирования до создания инновационных решений. Студенты углубляются в теорию и практику алгоритмов, математики и структур данных. Тренинг обеспечивает не только техническую базу, но и готовит выпускников к решению реальных задач.
Стоимость: 5434 руб./мес.
Длительность: 6 месяцев
Формат обучения: онлайн-лекции, практические задания, дипломный проект
Сертификат: диплом о переподготовке
Преимущества:
теория и практика по алгоритмам, математике и структуре данных;
диплом о повышении квалификации и оценке 7 проектов;
тренинг разработан профессионалами из Германии, Японии, Израиля;
проверка кода каждого проекта для получения подробных консультаций и оптимизации;
365 дней поддержки наставника;
гарантированное трудоустройство на 6 месяцев.
Недостатки:
некоторые докладчики сложно объясняют материал;
возможны задержки с проверкой домашних заданий.
Программа обучения:
Основы программирования
Python и фреймворки
ООП
Базы данных
Git и GitHub
Автотестирование
Программа создана для тех, кто стремится стать высококвалифицированным специалистом по DevOps. Вы начнете с освоения системного администрирования, а затем глубже погрузитесь в DevOps. Студенты осваивают Docker, Terraform, Ansible и Kubernetes, приобретая навыки для автоматизации процессов и управления инфраструктурой.
Стоимость: 6309 рублей в месяц
Длительность: 12 месяцев
Формат обучения: видео и текстовые материалы, практические задания, тестирование
Сертификат: да
Преимущества:
ориентация на требования работодателей в 2022 году;
возможность постоянного доступа к обновлениям тренинга;
6 проектов в портфолио;
гарантированное трудоустройство или возврат денег.
Недостатки:
медленные ответы кураторов на вопросы;
не все темы снабжены домашними заданиями.
Программа обучения:
Основы Python.
Старт в DevOps: системное администрирование для начинающих
DevOps-инженер. Основы
DevOps-инженер. Advanced
Система контроля версий Git
Основы Python
Язык запросов SQL
Docker
Уроки для тех, кто уже обладает базовыми знаниями SQL и Linux Авторы рассматривают ClickHouse от базовых концепций до продвинутых решений. Слушатели узнают, как установить и настроить, работать с базовыми и расширенными функциями, отличиями от других СУБД и использовать ее в различных сценариях.
Стоимость: от 7700 рублей в месяц
Длительность: 3 месяца
Формат обучения: вебинары, домашние задания, тестирование
Сертификат: да
Преимущества:
практические задания для закрепления знаний;
бесплатная консультация после сессий AMA и домашних заданий;
прохождение тренинга в назначенное время или в индивидуальном темпе.
Недостатки:
много теории на вебинарах;
избыток домашних заданий.
Программа обучения:
Знакомство с ClickHouse
Работа с ClickHouse
Масштабирование и манипуляции с данными
Управление ресурсами
Популярные интеграции
Проектная работа
7. Microservices - паттерны | PurpleSchool
Для тех, кто стремится освоить архитектуру микросервисов, начиная с декомпозиции проекта и заканчивая реализацией бизнес-логики. Антон Ларичев, автор тренинга, предоставляет уникальный опыт внедрения микросервисов из реальных проектов. Студенты учатся работать с такими технологиями, как Docker, RabbitMQ, NestJS, и погружаются в асинхронную архитектуру. На фоне множества практических заданий тренинг дает не только знания по разработке TypeScript для Node.js, но и теоретическую основу для успешного внедрения микросервисов.
Стоимость: от 2999 рублей
Длительность: 14 часов
Формат обучения: записи лекций, домашние задания, тестирование
Сертификат: да
Преимущества:
30-дневная гарантия возврата денег;
постепенное освоение материала от простого к сложному;
поддержка опытного наставника;
возможность добавлять проекты в портфолио;
практический опыт разработки;
последовательное изучение серверной разработки.
Недостатки:
возможные трудности в организационных вопросах;
ограниченное совместное обсуждение заданий с сокурсниками.
Программа обучения:
Настройка окружения
Нотация описания
Монорепозитории
Обмен сообщениями
RabbitMQ
Реализация взаимодействия
Реализация API
Реализация бизнес логики
Консистентность данных
Разработанный для бизнес-аналитиков и системных аналитиков, архитекторов и лидеров цифровой трансформации, тренинг дает представление о всеобъемлющем взгляде на корпорацию. Преподаватели объясняют ключевые элементы метода разработки архитектуры TOGAF, обучая анализу компании на всех уровнях. Студенты узнают, как создавать, координировать и поддерживать целевое видение компании, а также адаптировать метод TOGAF к гибким организациям. Уроки направлены на преодоление разрыва между стратегией и реализацией, предоставляя современное понимание бизнес-архитектуры.
Стоимость: от 7875 рублей в месяц
Длительность: 4 месяца
Формат обучения: онлайн-лекции, практические задания, итоговый проект
Сертификат: да
Преимущества:
вебинары с преподавателями-практиками;
обучение на реальных примерах;
возможность онлайн-обучения с записью вебинаров для пропущенных занятий;
разработка собственного проекта;
практика с экспертами из различных отраслей;
мастер-классы;
поддержка преподавателя-куратора.
Недостатки:
длинные лекции, требующие больших временных вложений;
сложные практические задания.
Программа обучения:
Архитектура и стратегия
Бизнес-архитектура
Архитектура информационной среды
Техническая архитектура
Архитектурное исследование
Управление архитектурой и реализацией изменений
Развитие компетенций архитектора
9. Полный путь от монолита на Rails к микросервисной архитектуре | Thinknetica
В этой программе представлен полный путь от монолитного приложения Rails к архитектуре. Предназначен для тех, кто стремится освоить принципы построения микросервисов на Ruby, включая создание, тестирование и объединение их в единую систему. Автор Евгений Фатеев, RoR-разработчик с более чем 10-летним стажем, предоставляет уникальный опыт и конкретные инструменты для успешного внедрения микросервисов. Тренинг включает в себя теоретическую базу и практические навыки.
Стоимость: от 35 900 рублей
Длительность: 9 недель
Формат обучения: скринкасты с уроками, домашние задания
Сертификат: да
Преимущества:
обучение от ведущих разработчиков и руководителей команд с большим опытом работы;
уникальная информация;
гарантия возврата денег в течение 14 дней;
возможность применения лучших подходов и наработок в работе.
Недостатки:
большая нагрузка во второй части тренинга;
очень ограниченное время для заключительной проектной работы.
Программа обучения:
Архитектура и принципы создания приложений
Ruby-микросервисы
Логирование
Деплой
Мониторинг
10. Микросервисная архитектура | IBS Training Center
Этот тренинг поможет вам принимать обоснованные решения о ее использовании в зависимости от потребностей бизнеса. Он рассматривает основные проблемы, механизмы и шаблоны для их решения, а также знакомит с такими инструментами, как Kubernetes и Istio. Практические занятия под руководством эксперта в области разработки программного обеспечения Александра Лавыша дают студентам практические навыки проектирования систем ISA. Тренинг предназначен для архитекторов, специалистов DevOps и ведущих разработчиков, предоставляя не только теоретические основы, но и реальные практические навыки проектирования и внедрения ISA.
Стоимость: 48 900 рублей
Длительность: 24 академических часа
Формат обучения: онлайн-лекции, домашние задания
Сертификат: да
Преимущества:
широкий кругозор преподавателя обогащает учебный процесс;
тренинг охватывает многие аспекты, включая примеры из реального мира;
групповые задания.
Недостатки:
лекции могут выходить за обещанные временные рамки;
теоретическая база может быть не совсем полной.
Программа обучения:
Архитектурные стили
Паттерны декомпозиции MSA
Организация разработки под MSA
Интеграция
Организация работы с данными в MSA
Основные шаблоны МСА
Развертывание
Стратегии миграции от монолита к MSA
Архитектура микросервисов — это инновационный подход к разработке программного обеспечения, который позволяет разработчикам эффективно организовывать свои усилия в рамках автономных команд. Каждая команда отвечает за разработку и поддержку одного или нескольких сервисов, действуя полностью независимо. Такая декомпозиция обеспечивает гибкость и ускоренную разработку, позволяя вам использовать различные технологические стеки при создании новых сервисов или внесении существенных изменений в существующие. Мы предлагаем бесплатные курсы по микросервисной архитектуре для начинающих, которые дают базовые практические знания для освоения этого подхода.
1. Что такое микросервисы от Maksim Zhashkevych
Этот видеотренинг на YouTube продолжительностью около полутора часов дает углубленный обзор теории архитектуры микросервисов. Ведущий, Максим Жашкевич, делится своим опытом. В видео раскрываются основные принципы архитектуры микросервисов, объясняется, как разделение приложения на автономные компоненты может ускорить разработку и сделать ее более гибкой и управляемой.
Основные характеристики:
продолжительность: около 1,5 часов;
принципы архитектуры микросервисов;
применимость микросервисов для больших команд, сложных проектов, нестабильного трафика и частых обновлений приложений.
2. Микросервисная архитектура базовые знания от Laravel Creative
Видеоурок на YouTube продолжительностью около полутора часов дает базовые знания по архитектуре микросервисов. От введения в концепцию монолита до подробного рассмотрения плюсов и минусов микросервисов, тренинг обеспечивает четкое понимание основ этой архитектуры. Этот тренинг предназначен для начинающих разработчиков и тех, кто интересуется веб-разработкой. Он предоставляет базовые знания об архитектуре микросервисов, помогая студентам понять отличия от монолита и освоить основные принципы создания приложений в стиле микросервисов.
Основные характеристики:
структурированный обзор: от введения до практического задания;
анализ плюсов и минусов монолита и микросервисов;
пример приложения с микросервисами;
рекомендации и терминология архитектуры микросервисов.
3. Все про Микросервисы от Sergei Calabonga
Тренинг включает в себя обзор шаблонов, советов, правил, рекомендаций и решений для успешного внедрения микросервисной системы. Канал ориентирован на разработчиков и программистов, которые хотят стать архитекторами информационных систем. Он подходит как для тех, кто только начинает свой путь в программировании, так и для опытных разработчиков.
Основные характеристики:
продолжительность: от полутора часов до получаса на видео;
обширный охват: шаблоны, правила, рекомендации, решения;
опытный руководитель: Сергей Калабонга — программист с опытом работы с 1989 года;
новые элементы и темы: от ASP.NET до облачных шаблонов проектирования.
Знакомство с архитектурой микросервисов открывает новые горизонты, позволяя вам эффективно разрабатывать, тестировать и масштабировать сервисы независимо друг от друга. Курсы микросервисной архитектуры предоставляют множество вариантов обучения с учетом таких критериев, как цена, опыт преподавателя, количество практики и другие важные параметры.
Больше курсов по микросервисной архитектуре смотрите в нашем каталоге.
Получив квалифицированную консультацию экспертов, выполнив практические задания и реальные проекты, вы сможете развить необходимые профессиональные навыки для успешного трудоустройства.
Делимся с вами кусочком нашей PROкачки! Валерий, C#-разработчик, рассказал что такое микросервисы, как работает технология, какие у нее есть плюсы и минусы: https://youtu.be/wiI84n1Znss
Будет полезно всем, кто хочет разобраться в микросервисах😉
В конце поднимаем извечный спор: что лучше - микросервисы или монолит? На какой стороне вы?
Конкурс мемов объявляется открытым!
Выкручивайте остроумие на максимум и придумайте надпись для стикера из шаблонов ниже. Лучшие идеи войдут в стикерпак, а их авторы получат полугодовую подписку на сервис «Пакет».
Кто сделал и отправил мемас на конкурс — молодец! Результаты конкурса мы объявим уже 3 мая, поделимся лучшими шутками по мнению жюри и ссылкой на стикерпак в телеграме. Полные правила конкурса.
А пока предлагаем посмотреть видео, из которых мы сделали шаблоны для мемов. В главной роли Валентин Выгодный и «Пакет» от Х5 — сервис для выгодных покупок в «Пятёрочке» и «Перекрёстке».
Реклама ООО «Корпоративный центр ИКС 5», ИНН: 7728632689
Мы продолжаем разрабатывать систему заметок с нуля. В третьей части серии материалов мы познакомимся с графовой базой Neo4j, напишем CategoryService и реализуем клиента к новому сервису в APIService.
В первой части мы спроектировали систему и посмотрели, какие сервисы требуются для построения микросервисной архитектуры.
Во второй части мы спроектировали и разработали RESTful API Service на Golang cо Swagger и авторизацией.
Теперь разработаем сервис управления категориями CategoryService. Категории мы делаем в виде дерева с большой вложенностью, в теории — бесконечной. Сервис будем разрабатывать на языке Python, а в качестве хранилища используем Neo4j.
Исходный код проекта на Github
Подробности в видео и текстовой расшифровке под ним.
Запуск графовой базы данных Neo4j
Neo4j — это высокопроизводительная NoSQL база данных, основанная на принципе графов. В ней нет такого понятия, как таблицы со строго заданными полями. Она оперирует гибкой структурой в виде нод и связей между ними.
Запускать Neo4j мы будем в Docker. Готовые образы на есть hub.docker.com. Они делятся на два вида: Enterprise и Community-версии. Нам надо выбрать тег образа, который будем использовать.
Важно: старайтесь не использовать тег latest, чтобы избежать ситуаций, когда вы запустите обновление контейнера, он обновится на последнюю версию, и у вас все сломается. Это связано с тем, что последнее обновление может содержать несовместимые изменения с вашей текущей версией. Например, у GitLab есть целая политика обновления как мажорных, так и минорных версий. Обновиться с 11 на 13 версию сразу не получится, придётся пройти длинный путь последовательных обновлений: 11.5.0 -> 11.11.8 -> 12.0.12 -> 12.10.14 -> 13.0.12 -> 13.2.3.
Но вернёмся к Neo4j. В официальной документации есть статья про запуск в Docker-контейнере. Возьмём оттуда команду docker run и преобразуем её в простой docker-compose.
docker run \
--name testneo4j \
-p7474:7474 -p7687:7687 \
-d \
-v $HOME/neo4j/data:/data \
-v $HOME/neo4j/logs:/logs \
-v $HOME/neo4j/import:/var/lib/neo4j/import \
-v $HOME/neo4j/plugins:/plugins \
--env NEO4J_AUTH=neo4j/test \
neo4j:latest
Версия docker-compose — 3.9. Затем идёт определение сервисов. У нас будет сервис Neo4j. Укажем, чтобы он всегда рестартовал, если упадет. Образ мы будем использовать с тегом 4.2.3. Далее — имя контейнера. Указываем порты, которые мы пробрасываем на хостовую машину. На порте 7474 будет доступен веб-интерфейс по протоколу HTTP, а 7687 используется для общения с Neo4j по протоколу BOLT. Далее пробрасываем volumes. Кроме тех, что указаны в команде, я ещё пробросил /var/lib/neo4j/conf, чтобы был доступ в файлу конфигурации neo4j.conf.
По умолчанию Neo4j создает пользователя neo4j с паролем neo4j и при первом входе требует сменить пароль. Этот этап можно пропустить, задав пароль через переменную среду NEO4J_AUTH.
Готовый файл docker-compose запускаем командой:
docker-compose -f /path/to/docker-compose.yml
Дополнительно нам понадобятся процедуры APOC, которые расширяют функционал запросов к Neo4j. Для этого идём в репозиторий этих процедур, скачиваем jar-файл apoc-4.2.0.2-all.jar и кладём в наш volume ./export/neo4j/plugins.
Работа через веб-интерфейс Neo4j
После установки Neo4j открываем веб-интерфейс, который висит на порте 7474. Коннектимся к дефолтной базе Neo4j. Вводим пароль, который указали в compose-файле. Нас встречает красивый интерфейс с тёмной темой. Он состоит из нескольких секций:
Editor — здесь вводим команды на языке Cypher, чтобы работать с графом. Хоткей Shift + Enter позволяет вводить команду в несколько строк. Чтобы запустить выражение, нажмите Ctrl + Enter (CMD + Enter). Также ведётся полная история всех введённых команд.
Stream — для отображения результата выполненной команды. Для каждой выполненной команды создаётся отдельный Stream.
Code отображает все полученные и отправленные данные от сервера.
Sidebar отображает мета-дату базы данных и основную информацию. Сохранённые скрипты можно раскидать по папкам. Также тут размещены ссылки на документацию и информация о лицензии.
Веб-интерфейс Neo4j
Основные команды языка Cypher
Язык Cypher создан специально для взаимодействия с графами. Он является декларативным, похож на SQL и использует паттерны, чтобы описать граф. Давайте разберёмся с основными командами, которые нам нужны.
CREATE (u:User {id: 1})
Это команда создания ноды, которая будет представлять пользователя. CREATE — это ключевое слово, после него идет определение ноды в круглых скобках, u — это алиас, который мы можем использовать в запросе, он не сохраняется в базу. Далее через двоеточие указываем лейбл, их можно указать несколько. Они сохраняются в базе и могут быть использованы для идентификации. После лейблов в фигурных скобках в формате JSON указываются свойства ноды.
Выполняем команду, нажимая Cmd + Enter. Создаётся новое окно стрима, где отображается результат работы нашей команды. В данном случае в стриме будет отчёт о том, что было создано.
Теперь давайте найдём нашу ноду. Пишем ключевое слово MATCH, далее определяем, какие ноды ищем и возвращаем по алиасу.
MATCH (u:User:Human) RETURN u
Теперь создадим ещё одну ноду и отношение с нодой пользователя.
MATCH
(u:User)
WHERE u.id = 1
CREATE (c:Category { name: "Category 1", id: apoc.create.uuid()})
CREATE (u)-[r:OWN]->(c)
RETURN c.id
Пишем ключевое слово MATCH и ноду, далее идёт ключевое слово WHERE — ищем пользователя с id = 1. После нахождения мы можем создать новую ноду:
Пишем CREATE.
Формализуем ноду «Категории».
Указываем свойство id и вызываем функцию APOC, которая генерирует UUID. Чтобы она работала, мы копировали ранее jar-файл.
После создания ноды создаём отношение:
Добавляем ключевое слово CREATE.
Пишем ноду нашего юзера.
Создаём отношение.
Возвращаем id созданной категории.
Отношение состоит из 3 элементов: направление отношения по отношению к пользователю, алиас и лейбл, направление отношения по отношению к ноде категории. То есть мы можем создать отношение как от юзера к категории, так и от категории к юзеру. У отношений также могут быть свойства. Они задаются в формате JSON. Это крайне удобно для сложных структур. Например, можно было для отношений пользователя к группам и категорий к подкатегориям сделать свойства отношения created_date.
Чтобы посмотреть, как Neo4j обрабатывает наш запрос, добавим ключевое слово EXPLAIN или PROFILE.
PROFILE MATCH (n)
RETURN n
EXPLAIN MATCH (n)
RETURN n
Мы увидим план запроса, который можно проанализировать, чтобы понять, насколько он хорош или плох.
План запроса
Посмотреть историю команд можно командой :history, очистить все стримы — командой :clear.
Давайте создадим ещё немного нод и отношений, чтобы можно было выполнить запрос на поиск всех нод и всех отношений:
MATCH (n)
RETURN n
У нас получилось создать две ноды пользователей с одинаковым полем id, так как уникальным идентификатором по умолчанию является внутренний идентификатор ноды , который генерирует сам Neo4j.
Запросы, которые будут использоваться в нашем сервисе
Создание ноды пользователя мы рассмотрели. Но мы будем делать это вместе с командой создания рутовой категории следующим образом:
MERGE (u:User {id: 1})
CREATE (c:Category { name: "Category 22", id: apoc.create.uuid()})
CREATE (u)-[r:OWN]->(c)
RETURN c.id
Используем ключевое слово MERGE, которое создаст новую ноду, если пользователя с таким id нет, или найдёт все ноды с таким id. Создаём новую ноду «Категории» и связь между нодой пользователя и категории. В результате мы видим два JSONа и две созданные ноды с одинаковым названием, потому что первый запрос на матчинг пользователя нашёл двух пользователей с id равным единице и создал две категории и два отношения.
Рутовая категория — это категория, которая имеет связь с пользователем. Под категорией я имею в виду категории, которые не связаны с пользователем.
Созданные категории
Теперь нам нужен запрос на создание ноды подкатегории и отношения с рутовой категорией:
MATCH (cs:Category)
WHERE cs.id = "de8dc382-67bb-454c-9873-fd969eec1535"
CREATE (c:Category { name: "Category 1.2", id: apoc.create.uuid()})
CREATE (cs)-[r:CHILD]->(c)
RETURN c.id
Находим ноду категории по идентификатору. Далее создаём категорию и отношение между категориями. Для связи используем лейбл CHILD, а не OWN как для отношения между нодами пользователя и рутовой категории.
Нам также нужен запрос, который вернёт дерево всех нод категорий со всеми подкатегориями пользователя:
MATCH path = (u:User)-[*]->(c)
WHERE NOT (c)-->() AND u.id = "77803c1a-8c1a-492a-89be-f219735b2aef"
RETURN path
Мы ищем от стартовой ноды, пока не дойдём до ноды, у которой нет выходных связей. В MATCH мы пишем path =. Таким образом мы создаём объект с типом Path, в котором находятся все найденные ноды. Это упрощает возврат всего графа всех категорий. Этот запрос отлично выводит граф в Neo4j в браузере, но для кода нам нужен на выходе древовидный JSON. Для этого соберём path в список и отдадим его функции apoc.convert.toTree, чтобы получить выходной древовидный JSON. Но перед его выводом удалим вторую ноду пользователя по внутреннему идентификатору:
MATCH (u:User)
WHERE ID(u) = 26
DETACH DELETE u
Теперь модифицируем нашу команду для получения дерева категорий. Добавляем ключевое слово WITH, собираем наш путь в список нод и вызываем функцию apoc.convert.toTree, передавая на вход список нод. В результате получаем древовидный JSON.
MATCH path = (u:User)-[*]->(c)
WHERE NOT (c)-->() AND u.id = "77803c1a-8c1a-492a-89be-f219735b2aef"
WITH collect(path) AS ps
CALL apoc.convert.toTree(ps) yield value
RETURN value
Ещё нам нужно проверять наличие родительское категории, чтобы понимать, сможем ли мы создать подкатегорию. Сделать это можно при помощи следующего запроса:
OPTIONAL MATCH (n:User{id:2})
RETURN n IS NOT NULL AS Predicate
В результате выполнения запроса вернётся true или false.
Также нам нужно каскадно удалять категории. Чтобы сделать это, мы ищем все ноды от стартовой категории, которую хотим удалить, убираем связи и удаляем их.
MATCH path = (c:Category)-[*]->(cc:Category)
WHERE c.id = "95bec604-5da2-4297-b792-5a866e292df4"
DETACH DELETE path
Во время разработки мне очень пригодился запрос на удаление всех нод. Ключевое слово DETACH позволяет удалять связанные ноды каскадно.
MATCH (n)
DETACH DELETE n
Создание CategoryService
Писать будем на Python.
Маленький disclaimer: я на Python пишу в таком же стиле, как на Java, так как Java был моим первым языком программирования. Знаю, что это не дзен и что так не принято, но такой код легче тестировать, в нём меньше ошибок, он более читаемый.
Сначала создадим venv и установим нужные библиотеки. Будем использовать:
Flask как минималистичный веб-фреймоворк.
Flask-CORS для возможности обращения с разных доменов.
Flask-RESTful, чтобы было удобнее создавать классы с ресурсами. Этот проект забросили, если знаете достойную замену, напишите, пожалуйста, в комментариях.
Flask-Injector для реализации паттерна Dependency Injection, чтобы не было глобальных импортов.
PyYAML для работы с форматом данных YAML.
Начнём с главного файла app.py и с конфига приложения.
Создаём класс конфига. Почти везде я буду использовать слоты для классов. Это уменьшает вес объектов, но лишает объекты свойства dict, об этом нужно помнить.
Конфиг мы будем вычитывать из YAML-файла. Поэтому в конструкторе получаем путь до файла и сразу вызываем функцию read, которая вычитает файл.
Проверяем, что такой файл есть. Если его нет, то выбрасываем ошибку.
В контексте открываем файл и вычитываем содержимое. Тут же парсим его при помощи библиотеки PyYAML.
Проходимся по содержимому.
Далее мы будем вызывать метод setattr, чтобы засетить слоты в нашем классе. Если ключ есть в слотах, мы сеттим этот атрибут.
Создадим YAML-файл с конфигурацией. В нашем конфиге будет переменная debug. Она нужна для разработки, например, дополнительного логирования или перехвата ошибок.
Для констант создаём файл constants.py. Там мы будем хранить все константы приложения, например, путь до конфиг-файла и путь до папки с логами.
Возвращаемся к app.py. Следующий важный компонент — это логирование.
Создаём дефолтный логер с уровнем DEBUG.
Создаём FileHandler, чтобы наши логи попадали в файл.
Формат записи в логе мы определяем при помощи форматтера. У него особый язык, который описан в документации.
Добавляем в логер наш хендлер.
Создаём основной объект нашего приложения app, настраиваем его, а конфиг сеттим внутрь нашего объекта. Это позволит конфигурировать сам Flask через наш конфиг.
Оборачиваем объект приложения объектом Api из библиотеки Flask-RESTful. Теперь мы сможем регистрировать ресурсы в объекте api.
Оставляем место, где у нас будет injector для Dependency Injection.
Пишем глобальную обработку ошибок.
Метод errorhandler будет перехватывать все исключения типа AppException и вызывать функцию app_exception_handler. Теперь нам нужно создать такой класс исключения. Наследовать его будем от класса Exception. Также мы сделаем систему ошибок в нашем приложении. AppException в конструкторе будет принимать на себя параметр exc_data, который может являться enum AppError и состоит из полей error: сообщение об ошибке, error_code — код ошибки и developer_message — сообщение для разработчика.
Различные кастомные обработчики я обычно называю помощниками. Поэтому создаю пакет helpers и там в файле flask.py реализую функцию обработки AppException. Также, если приложение не в дебаг-режиме, то мы перехватываем вообще все исключения, даже те, которые не обрабатываем. Например, где-нибудь мы поделим на 0 и не обработаем. Это нужно для продакшен-режима, когда пользователь не должен видеть разные HTML-страницы с ошибками или трейсы. Для этого создаём функцию uncaught_exception_handler, которая пишет в лог и вызывает функцию обработки AppException с ошибкой системы. У нас её как раз нет, поэтому возвращаемся и реализовываем enum AppError, добавляем в него SYSTEM_ERROR.
Мы будем добавлять в enum ошибки по мере их появления. Запускаем приложение и проверяем, что в конфиге есть все наши настройки.
Разработка DAO-слоя для доступа к Neo4j
Мы будем реализовывать классический DAO-паттерн. Будет абстрактный класс Storage, который является по сути интерфейсом с абстрактными методами, и конкретная реализация с Neo4j. Это позволит не привязываться к конкретному хранилищу. Если возникнет необходимость его сменить, это можно будет сделать, создав ещё одну реализацию и изменив класс в контейнере зависимостей.
В классе с реализацией для Neo4j мы создаём объект Graph и передаём ему параметры для подключения. Для понимания того, что надо передать классу, чтобы присоединиться, есть два пути: смотреть в документацию или в исходники. Оба имеют право на жизнь.
Выполнение любого запроса для Neo4j — это исполнение команды Cypher. Создаём метод execute_cypher и все методы реализуем одинаково: вызываем метод execute_cypher и возвращаем прочитанные данные из курсора. С первого взгляда выглядит как костыль. Но это условность, которая даёт гибкость в смене хранилища, при этом проблем с реализацией или тестируемостью нет.
Далее создаём ресурсы категорий. Библиотека Flask-RESTful даёт возможность создавать ресурсы в виде классов с методами get, post, put, delete. Создав метод get, понимаем, что нам нужен сервис управления категориями, а создав класс сервиса, понимаем, что ему нужен DAO для доступа к категориям. Создаём класс-интерфейс CategoryDAO, который мы и будем инжектить, и реализацию этого класса под Neo4j.
Пришло время запросов, которые мы рассмотрели ранее.
Идём в Neo4j и ещё раз проверяем, что запросы работают корректно.
Переносим запрос поиска всех категорий пользователя из Neo4j в код сервиса. Попутно везде проставляем возвращаемые значения, где забыли.
Возвращаемся в сервис, добавляем в конструктор логер и реализуем метод получения категорий.
Разбираемся с методами сервиса и DAO-слоя. Основная логика простая: сервис вызывает метод DAO и получает данные, с которыми он может что-то сделать и вернуть.
Надо протестировать хотя бы получение всех категорий. Но для этого нам надо в сервисе получить реализацию интерфейса CategoryDAO. Это значит, что пришло время Dependency Injection.
В основном файле app.py создаём объект Injector, который на вход принимает массив.
Создаём объект FlaskInjector, которому отдаём наш объект app и созданный Injector.
Создаём отдельный файл di.py.
В файле di.py создаём классы модулей для наших зависимостей.
В случае со StorageModule для создания объекта Neo4jStorage нам нужны данные подключения к Neo4j из конфига. Поэтому в конструкторе передаем конфиг, а в методе configure уже используем его для создания экземпляра класса Neo4jStorage и биндим интерфейс Storage на объект нашей реализации. Также указываем параметр scope со значением singleton, то есть один объект на все приложение. Кроме того, там можно указать request, и тогда объект будет создаваться на каждый запрос к API. Таким же способом биндим интерфейс CategoryDAO на реализацию Neo4jCategoryDAO. Далее создаём подобный модуль для логера, чтобы и его можно было инжектить в конструкторе классов.
Заполняем массив Injector в файле app.py нашими модулями и проставляем декоратор @inject над конструкторами, куда инжектим наши классы. В ресурсах мы инжектим CategoryService, в CategoryService инжектим CategoryDAO, а в реализации Neo4jCategoryDAO инжектим Storage.
У нас будет два ресурса: CategoriesResource и CategoryResource.
Реализуем метод GET у ресурса списка категорий.
Вызываем сервис категорий, чтобы получить список категорий пользователя.
Протестируем этот метод. Создаём HTTP скетч и делаем запрос.
Первый тест сразу покажет все ошибки и опечатки. После исправлений проходимся в дебаге по всему процессу и смотрим, что происходит.
Мы успешно получили данные из Neo4j. Парсим полученные данные от стораджа.
Тут надо отметить, что ключ value — это стандартный ключ, генерируемый Neo4j, а вот ключ own — это уже связь между пользователем и его рутовой категорией. У категории есть ключ child, что обозначает связь между рутовой категорией и подкатегорий.
Чтобы распарсить древовидный JSON, нам очевидно нужна рекурсивная функция.
Создаём функцию _parse_categories и понимаем, что нам не хватает модели Category.
Создаём пакет model в DAO и в нем организуем три файла: base.py для базовой модели методов, dto.py для реализации паттерна DTO и model.py для данных из БД.
DTO — это паттерн для оперирования входными данными от пользователя. Они упаковываются в класс DTO. Сервисы работают с ними, а модель — это уже полное представление данных из БД. Реализовывать модели и DTO будем на data-классах.
При реализации модели Category есть один нюанс. Так как категория содержит в себе подкатегории, то модель должна содержать поле с массивом это же класса. Python так не позволяет сделать, так как он не видит этого класса, для него он не создан. Обойти это можно строковой аннотацией и объектом List из пакета Typing. Все классы будем наследовать от базового класса Base. У него будут общие методы, например, преобразование объекта в словарь, исключая пустые поля. Это нужно, чтобы в итоговом JSON не было пустых полей.
DTO создаются на каждое действие пользователя. В нашем случае это Create, Update и Delete. Разные DTO содержат разные данные и разные обязательные поля. Например, для CreateCategoryDTO не нужен uuid категории, так как его еще не существует. В вот для Delete и Update uuid категории обязательно нужен.
Возвращаемся к парсингу категорий. Тут всё просто.
Проходим по списку, собираем категорию.
Если у категории есть дети, рекурсивно вызываем функцию, чтобы получить список категорий детей категории.
Доделываем метод get_categories у сервиса категорий.
Делаем проверку на то, что пользователь, чьи категории запрашиваются, существует, иначе отдаем 404 и ошибку USER_NOT_FOUND.
Реализуем методы создания, обновления и удаления категории. Я вызываю несуществующие методы, чтобы понять, какие методы будут нужны. Также попутно дополняю DTO-классы необходимыми полями, после чего массово объявляю все нужные методы в интерфейсе CategoryDAO и потом реализую эти методы в DAO-реализации для Neo4j.
Запросы всё так же тестируем в веб-интерфейсе Neo4j. Графы выглядят очень красиво, а язык Cypher крайне удобен, нет проблем с запросами бесконечной вложенности.
Есть информация, что Neo4j падает при 1 миллионе нод и 500 связей между ними. Я не тестировал, но думаю, что если применить масштабирование и/или шардирование, то эту проблему можно решить.
После реализации всех методов в DAO возвращаемся к ресурсам.
Получаем user_uuid из Query Path. Запускаем и проверяем получение всех категорий без указания user_uuid. Получаем код 500 вместо 404. Cтандартная ошибка вместо нашего User not found. Начинаю разбираться, почему ошибка не моя:
Проверяю функции обработки ошибок.
Понимаю, что в ошибке не хватает HTTP-кода возврата.
Нахожу ошибку в классе исключения. Я не забирал данные из переданной ошибки в исключении.
После исправления я всё-таки перехватил ошибку. Но метод jsonify упал с ошибкой, так как не смог сериализовать объект AppException. Поэтому передаем в jsonify не сам объект, а вызываем у него magic-метод dict. Теперь всё хорошо, кроме HTTP-кода.
Проблема была в месте присваивания HTTP-кода в классе AppException. Ошибка корректная. Теперь проверяем получение всех категорий с заданным user_uuid. Видим, что все объекты получены корректно. Но теперь код падает на сериализации списка категорий. Проблема в том, что стандартный сериализатор JSON не знает, что делать с классом Category, надо ему рассказать об этом.
Создаём в пакете helpers файл json.py. В нём создаём класс CustomJSONEncoder с методом default. Здесь мы пытаемся создать универсальный сериализатор, который будет уметь сериализовать строки, энамы, словари, итерируемые объекты, объекты со слотами или без слотов, но с объектом dict. Функция должна рекурсивной, чтобы сериализатор корректно работал с вложенными объектами. Поэтому выделяем код в отдельный метод to_dict и рекурсивно вызываем на каждый объект внутри объекта, а метод to_dict вызываем в методе default. Проверяем еще раз — успех. Мы получили древовидный JSON.
Тут можно спросить, зачем я десериализую JSON от Neo4j в объекты, а потом опять сериализую его в JSON, чтобы отдать клиенту. Ответ простой: если потребуется реализовать какую-то логику по обогащению данных или изменится структура в хранилище, структура и модели данных не изменятся, а значит не изменится и контракт сервиса. В ролике про микросервисы я уже объяснял, что очень важно проектировать сервисы таким образом, чтобы их контракты не менялись даже при изменении логики работы самого сервиса.
Методы создания, обновления и удаления категории
Далее реализуем метод post для создания категории. Я не хочу вручную получать данные из body и создавать DTO. Поэтому применяю маршалинг. Пишу декоратор masrshal_with, в котором указываю, что вернёт метод, и декоратор use_kwargs, для которого указываю, какую DTO я жду на входе. То есть этот декоратор возьмет body из запроса и провалидирует его относительно data-класса, который я ему задал в параметрах.
Чтобы это работало, нужно реализовать метод to_schema во всех DTO. Для этого в классе Base реализуем метод to_schema. Воспользуемся библиотекой marshmallow_dataclass и методом class_schema. Теперь в сигнатуре метода хендлера я могу объявить аргумент валидируемой DTO. В методе post создания категории мы ничего не возвращаем, кроме заголовка Location с URI до созданной категории. Из этого заголовка будет легко получить uuid созданной категории. Пишем тест на создание рутовой категории (то есть без параметра parent_uuid) и проверяем. Видим, что наша DTO десерелиазована и все данные на месте. Главное, что категория создана. Теперь пишем тест на создание подкатегории, указывая parent_uuid в body, и проверяем, что такая категория была создана.
Теперь создаём ресурс для работы с конкретной категорией, а также методами patch для обновления и delete для удаления. Пишем для них тесты, проверяем функционал. Также добавим наследования классов ресурсов от класса MethodResource. Это нужно для корректной работы декораторов и возможности последующей генерации swagger-файла на основе кода. Запускаем тест и получаем ошибку в формате HTML. Давайте обработаем такие ошибки отдельно. Создаём функцию handle_request_parse_error с декоратором parser.error_handler. В случае такой ошибки выполняем функцию abort и передаём ей ошибки схемы валидации. Повторяем тест и получаем JSON с ошибкой валидации схемы. Далее финальное тестирование, фиксим опечатки. Сервис готов.
Ещё можно сделать валидацию пользовательских данных, например, проверку формата uuid для полей user_uuid и parent_uuid. Также можно провалидировать имя категории, чтобы это была строка, ограничить её по длине, нормализовать и сделать ограничение на спецсимволы.
Доработка APIService
Теперь нужно сделать реализацию клиента к новому CategoryService в нашем APIService.
Создаём папку rest в pkg.
Создаём файл client.go, в котором у нас будут общие структуры для общения с RESTful-сервисом.
Добавляем FilterOptions — это структура для возможности фильтрации список, которая состоит из поля, оператора и значений.
Делаем метод BuildURL, который будет формировать итоговый URL из базового URL ресурса, к которому мы обращаемся, и фильтров, которые, по сути, являются ключами и значениями Query Path. В BuildURL мы парсим базовый URL, добавляем в него ключи и значения из фильтров.
Создаём метод ToStringWF у фильтра, чтобы склеить оператор и значения.
Добавляем структуру ошибки API с полями Error, ErrorCode и DeveloperMessage. Это те же поля, что были в классе AppError в CategoryService.
В internal создаём папку client.
В директории client создаём папку category и файл client.go.
В директории category создаём файлы model.go и service.go.
В файле model.go описываем структуру «Категории». Здесь уже нет проблемы с указанием собственного класса в полях класса, как это было в Python.
Применяем паттерн DTO и создаём DTO-модели.
Код можно немного зарефакторить. Идея следующая: в папке rest будет общий клиент для всех RESTful-сервисов в файле client.go, а в файле url.go — уже построение общего URL и структуры API.
Базовый клиент состоит из URL, HTTP-клиента и мьютекса для многопоточной работы.
Создаём метод SendRequest, который будет отправлять запрос и возвращать ответ.
Лочим мьютекс.
Запускаем отложенный анлок при помощи defer.
Проверяем, есть ли HTTP-клиент.
Выставляем заголовки, которые при желании можно получать на входе в функцию.
Выполняем запрос.
Проверяем статус-код у ответа. Если он плохой, то пытаемся прочитать из тела ответа ошибки. Также не забываем запустить отложенное закрытие тела ответа, так как это поток.
Реализуем метод Close, который обнуляет HTTP-клиент.
Теперь реализуем клиента к сервису категорий.
Создаём функцию NewClient, которая на вход получает URL сервиса.
Реализуем все нужные нам методы: получение, создание, обновление и удаление категорий.
Реализация довольно простая: получаем на вход нужные параметры, контекст и фильтры, дополняем фильтры нашей логикой. В случае получения категорий добавляем туда параметр user_uuid. Далее билдим URL и запрос, добавляем в запрос контекст и отправляем запрос. Закрытие тела в методе SendRequest надо делать, только если статус-код плохой. Иначе мы не вычитаем тело в нашем методе. Вычитываем тело ответа, ожидая там увидеть список категорий. Здесь нужно запустить отложенное закрытие потока тела ответа. Затем возвращаем список категорий. Добавляем щепотки логирования и реализуем остальные методы.
В Golang контекст является агрегацией действий и процессов. Он позволяет отменить их все. Например, пользователь сделал запрос, вы его получили, запустили в общем контексте два запроса и ещё несколько фоновых операций. В процессе работы пользователь отменил запрос. Нам нужно отменить операции. Чтобы не закрывать их все руками, вы просто вызываете метод Cancel у контекста. Все сделанные запросы также прервут своё выполнение.
В методах CreateCategory, UpdateCategory и DeleteCategory мы используем библиотеку structs, которая позволяет нам сгенерировать map из входной DTO-структуры. Полученный map мы маршалим. Получаем байты, которые отдаём запросу в виде буфера через конструкцию bytes.NewBuffer.
Когда клиент готов, добавляем его в структуру нашего хендлера категорий. В самом хенделере пишем код, который вызывает методы клиента сервиса категорий.
Во всех хендлерах, если вызов метода клиента категорий привел к ошибке, я отдаю код ответа «418 I’m a teapot». Использую его, чтобы обозначить, что запрос не выполнился из-за ошибки в коде, а не проблемы среды или недоступности БД. Также при получении списка категорий я начал реализовывать маршалинг структуры массива категорий и неожиданно понял, что просто так делаю сериализацию и десериализацию списка категорий.
В APIService я точно не буду обрабатывать данные списка категорий, а просто отдам их клиенту. Поэтому я вовремя одумался и зарефакторил код клиента, чтобы отдавать массив байт. В клиенте я их вычитываю методом ReadAll из пакета ioutil, а в хендлере сразу отдаю клиенту методом w.Write. Также удалил модель Category, так как она не нужна. При создании категории по всем правилам REST я отдаю заголовок Location с uuid созданной категории. Я мог бы не парсить заголовок от CategoryService, так как ендпоинты совпадают, но они могут перестать совпадать. Чтобы избежать багов, я решил распарсить сразу.
Также из контекста я забираю значение по ключу httprouter.ParamsKey и кастую в httprouter.Params. Значение в контекст кладёт библиотека роутера, которую я использую. Это удобный и быстрый способ получить значения из Query Path. В методах удаления и обновления категории я отдаю код ответа «204 No Content», а при создании категории отдаю «201 Created». Также в методе генерации токена доступа JWT я захардкодил UUID пользователя, который был создан в БД, чтобы протестировать решение. Этот хардкод уберётся, когда появится UserService.
В точке входа app.go я создаю клиента к CategoryService. Мне нужен URL до API сервиса. Его мы будем забирать из конфига, поэтому добавим эту настройку в конфиг, в код и в YAML- файл.
Начинаем серию видео по разработке системы заметок на Python, Golang и микросервисной архитектуры. Будем писать код и конфигурировать все используемы инфраструктурные продукты, такие как ElasticSearch, logstash, Kibana, zipkin, mongoDB, postgresql. И все это в докер контейнерах и с использованием docker compose.
В этом видео мы уточним функционал и спроектируем систему, разберемся с основными микросервисами и продуктами. которые будем использовать.
Ссылка на GitHub репозиторий: https://github.com/theartofdevel/notes_system
Вопрос: нужна ли текстовая расшифровка для этой стать и для будущих?
P.S. первая статья, не знаю правильно все сделал, если нет - поправьте в комментариях - все исправлю.
P.P.S. еще есть плейлисты с уроками по Go и Python, а также еще разный контент. Буду сюда тоже постить потихоньку.
Читаю обсуждения разработчиков на vc.ru и тут неожиданно кто-то рубит правду матку в обсуждении про базы данных и микросервисы.