Потому что жизнь слишком коротка, чтобы перерисовывать диаграммы Недавно я присоединился к новой компании в качестве . Как это всегда бывает, мне пришлось начинать с нуля. Например: где находится код живого приложения? Как он развертывается? Откуда конфиги? К счастью, мои коллеги проделали фантастическую работу по созданию «инфраструктуры как кода». И я поймал себя на мысли: инженера-программиста если все есть в коде, то почему нет инструмента, который мог бы соединить все точки? Этот инструмент проверит кодовую базу и построит диаграмму архитектуры приложения, выделив ключевые аспекты. Новый инженер может посмотреть на диаграмму и сказать: «Ну ладно, вот как это работает». Перво-наперво Сколько бы я ни искал, ничего подобного я не нашел. Наиболее близкими совпадениями, которые я нашел, были сервисы, рисующие диаграмму инфраструктуры. Некоторые из них я включил в , чтобы вы могли рассмотреть их поближе. В конце концов я бросил гуглить и решил попробовать свои силы в разработке чего-нибудь нового. этот обзор Сначала я создал пример приложения с помощью Gradle, и Terraform. Конвейер действий GitHub развертывает приложение в Amazon Elastic Container Service. Этот репозиторий будет исходным кодом для инструмента, который я создам (код ). Java- Docker здесь Во-вторых, я нарисовал очень общую диаграмму того, что я хотел увидеть в результате: Я решил, что будет два типа ресурсов: Реликвия Термин показался мне слишком перегруженным, поэтому я выбрал . Так что же такое Реликвия? Это 90% всего, что вы хотите увидеть. В том числе, но не ограничивается: «артефакт» Relic Артефакты (синие прямоугольники на схеме, т.е. Jars, Docker-образы), Конфигурации ресурсов Terraform (розовые квадратики на схеме, т.е. инстансы EC2, ECS, очереди SQS), ресурсы Кубернетеса, и многое-многое другое Каждая реликвия имеет имя (например, my-shiny-app), необязательный тип (например, Jar) и набор пар ключ → значение (например, путь → /build/libs/my-shiny-app.jar), которые полностью описывает Relic. Они называются . Чем больше определений будет у Relic – тем лучше. определениями Источник Второй тип — это . Источники определяют, создают или предоставляют Реликвии (например, желтые прямоугольники выше). Источник описывает Реликвию в каком-то месте и дает представление о том, откуда она взялась. Хотя источники — это компоненты, из которых мы получаем больше всего информации, на диаграмме они обычно имеют второстепенное значение. Вероятно, вам не понадобится много стрел, идущих от Terraform или Gradle к любой другой реликвии. Source Relic и Source имеют отношения многие-ко-многим. Разделяй и властвуй Охватить каждый фрагмент кода невозможно. Современные приложения могут иметь множество фреймворков, инструментов или облачных компонентов. Только в AWS имеется около 950 ресурсов и источников данных для Terraform! Инструмент должен быть легко расширяемым и не связанным по дизайну, чтобы другие люди или компании могли внести свой вклад. Хотя я большой поклонник невероятно подключаемой архитектуры поставщиков Terraform, я решил построить то же самое, хотя и упрощенное: У есть одна четкая обязанность: создание Реликвий на основе запрошенных исходных файлов. Например, читает файлы *.gradle и возвращает , или Relics. Каждый поставщик создает реликвии тех типов, о которых он знает. Провайдеры не заботятся о взаимодействии между Реликвиями. Они строят Реликвии декларативно, полностью изолированно друг от друга. Поставщика GradleProvider Jar War Gz При таком подходе можно легко проникнуть настолько глубоко, насколько захотите. Хорошим примером являются действия GitHub. Типичный YAML-файл рабочего процесса состоит из десятков шагов с использованием слабосвязанных компонентов и сервисов. Рабочий процесс может создать JAR-файл, затем образ Docker и развернуть его в среде. Каждый шаг рабочего процесса может быть охвачен его поставщиком. Итак, разработчики, скажем, создают Provider, связанный только с теми шагами, которые им интересны. Docker Actions Такой подход позволяет любому количеству людей работать параллельно, добавляя в инструмент больше логики. Конечные пользователи также могут быстро внедрить своих поставщиков (в случае использования какой-либо запатентованной технологии). Дополнительную информацию см. в разделе «Настройка» ниже. Соединять или не объединять Прежде чем перейти к самой пикантной части, давайте разберемся в следующей ловушке. Два Провайдера, каждый из которых создаёт по одному Реликту. Это нормально. Но что, если две из этих реликвий являются просто представлениями одного и того же компонента, определенного в двух местах? Вот пример. анализирует JSON определения задачи и создает Relic с типом . В рабочем процессе действий GitHub также есть шаг, связанный с ECS, поэтому другой поставщик создает реликвию . Теперь у нас есть дубликаты, потому что оба провайдера ничего друг о друге не знают. Более того, неверно считать, что кто-то из них уже создал Реликвию. И что? AmazonECSProvider AmazonECSTAsk AmazonECSTaskDeployment Мы не можем удалить ни один из дубликатов из-за определений (атрибутов), которые есть у каждого из них. Единственный способ — объединить их. По умолчанию следующая логика определяет решение о слиянии: relic1.name() == relic2.name() && relic1.source() != relic2.source() Мы объединяем две реликвии, если их имена одинаковы, но они определены в разных источниках (как в нашем примере, JSON в репозитории, а ссылка на определение задачи находится в действиях GithHub). При слиянии мы: Выберите одно имя Объединить все определения (пары ключ → значение) Создайте составной источник, ссылаясь на оба исходных источника. Нарисуйте линию Я намеренно опустил один важный аспект Реликвии. У него может быть — и лучше, чтобы он был! Matcher — это логическая функция, которая принимает аргумент и проверяет его. Сопоставители являются важными частями процесса связывания. Если реликвия соответствует какому-либо определению чужой реликвии, они будут связаны друг с другом. Matcher Помните, я говорил, что Провайдеры понятия не имеют о Реликвиях, созданных другими Провайдерами? Это все еще правда. Однако поставщик определяет сопоставитель для реликвии. Другими словами, он представляет собой одну сторону стрелки между двумя прямоугольниками на результирующей диаграмме. Пример. В Dockerfile есть инструкция ENTRYPOINT. ENTRYPOINT java -jar /app/arch-diagram-sample.jar С некоторой уверенностью мы можем сказать, что Docker все, что указано в . Итак, Relic имеет простую функцию Matcher: . Скорее всего, некоторые Relics с в Определениях будут соответствовать ему. Если да, появится стрелка между и Relics. контейнеризирует ENTRYPOINT Dockerfile entrypointInstruction.contains(anotherRelicsDefinition) Jar arch-diagram-sample.jar Dockerfile Jar После определения Matcher процесс связывания выглядит довольно простым. Служба связывания перебирает все реликвии и вызывает их функции Matcher. Соответствует ли Реликвия А какому-либо из определений Реликвии Б? Да? Добавьте ребро между этими реликвиями в полученный график. Край также может быть назван. Визуализация Последний шаг — визуализировать наш окончательный график предыдущего этапа. Помимо очевидного PNG, инструмент поддерживает дополнительные форматы, такие как , и . Эти текстовые форматы могут показаться менее привлекательными, но их огромное преимущество состоит в том, что вы можете вставлять эти тексты практически в любую вики-страницу ( , и многое другое). Mermaid Plant UML DOT GitHub , Слияние Вот как выглядит окончательная диаграмма примера репозитория: Кастомизация Возможность подключать собственные компоненты или настраивать существующую логику очень важна, особенно когда инструмент находится на начальной стадии. Реликвии и источники по умолчанию достаточно гибки; вы можете положить в них все, что захотите. Любой другой компонент настраивается. Существующие поставщики не покрывают необходимые вам ресурсы? Легко реализуйте свои собственные. Не устраивает описанная выше логика слияния или связывания? Без проблем; добавьте свою собственную или . Упакуйте все в JAR-файл и добавьте при запуске. Подробнее читайте . LinkStrategy MergeStrategy здесь Аутро Создание диаграммы на основе исходного кода, вероятно, получит распространение. И в частности инструмент (да, это название инструмента, о котором я говорил). ! NoReDraw Соавторы приветствуются Самым замечательным преимуществом (вытекающим из названия) является отсутствие необходимости перерисовывать диаграмму при изменении компонентов. Из-за отсутствия инженерного внимания документация в целом (и диаграммы в частности) устаревает. С такими инструментами, как , это больше не должно быть проблемой, поскольку их легко подключить к любому конвейеру PR/CI. Помните, 😉 NoReDraw жизнь слишком коротка, чтобы перерисовывать схемы