Продвигайте наглядность и стандартизацию для нескольких команд и проектов!

Объединение проектов в единый монорепозиторий стало новой тенденцией в разработке программного обеспечения, которая имеет широкий спектр преимуществ для рабочего процесса разработчика. Когда вы слышите термин MonoRepo, вы можете подумать об ужасной монолитной архитектуре с сильно связанными сервисами - но это не имеет ничего общего с MonoRepo! Монолитный репозиторий - это средство для объединения нескольких проектов в единую базу кода, чтобы разделить зависимости и стандартизировать рабочий процесс проекта для любой участвующей команды.

В этой статье мы собираемся обсудить некоторые особенности и недостатки MonoRepo, а также рассмотрим пример из реальной жизни. Мы также рассмотрим мыслительный процесс, лежащий в основе стандартизации процесса сборки с помощью Gradle. К концу вы можете обнаружить, что MonoRepo отлично подходит для ваших собственных проектов!

Структуры MonoRepo

Монолитное хранилище не должно соответствовать каким-либо строгим стандартам. По большей части, ваша собственная команда разработчиков решает, как они хотят структурировать свои проекты. Одним из основных преимуществ MonoRepo является то, что вам не нужно переключаться между загрузкой разных проектов в вашу среду IDE, просеиванием нескольких мест слияния (MR) или отдельными проектами, которые отвечают за общие зависимости. Вместо этого все находится в одном месте, чтобы добавить организацию и удобство!

Простой пример TypeScript

Начнем с примера MonoRepo, состоящего из двух проектов на основе TypeScript. Вы можете использовать Lerna или рабочие области пряжи для создания структуры MonoRepo и символической ссылки на пакеты в соответствии с зависимостями между ними. Настройка собственного MonoRepo с нуля потребует дополнительного планирования того, как вы будете связывать свои зависимости, поэтому инструменты, указанные выше, возможно, лучше подходят для вас. Вот пример MonoRepo, созданного с помощью Lerna и пряжи:

<Your Awesome Team Name>
├── package.json
├── packages
│   ├── app-web/
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   │   └── tsconfig.json
│   │   ├── test/
│   │   │   └── test.spec.ts
│   │   │   └── tsconfig.json
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── app-ios/
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   │   └── tsconfig.json
│   │   ├── test/
│   │   │   └── test.spec.ts
│   │   │   └── tsconfig.json
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
├── tsconfig.build.json
└── tsconfig.json

Если у вас есть службы, которые использовались как в app-web, так и в app-ios, вы можете связать эти два пакета с пакетом dependencies для дополнительной модульности. Вы не только объединяете проекты, но и стандартизируете структуру, чтобы членам вашей команды было легче переходить между пакетами.

Пример из реального мира

MonoRepos набирает обороты в технологическом пространстве, даже в таких гигантских технологических компаниях, как Google и Facebook. Фактически, MonoRepos от Google может содержать терабайты данных с миллионами строк кода. Я работаю в команде, которая сосредоточена на учетных записях пользователей и конечных точках служб - наш технический стек состоит из нескольких микросервисов, написанных на Go, а также нескольких проектов Angular. Вот общая инфраструктура, которую мы используем:

Mobile Services
├── <project_name>/
│   └── <service_name>/
│       ├── cmd/
│       │   └── main.go
│       ├── internal/
│       │   ├── config/
│       │   │   └── settings.go
│       │   │   └── settings_test.go
│       │   ├── endpoints/
│       │   │   └── endpoints.go
│       │   │   └── endpoints_test.go
│       │   ├── service/
│       │   │   └── service.go
│       │   │   └── service_test.go
│       │   └── transport/
│       │       ├── grpc/
│       │       └── http/
│       ├── .gitlab-ci.yml
│       ├── build.gradle.kts
│       ├── Dockerfile
│       ├── gradle.properties
│       ├── settings.gradle.kts
│       └── <service_name>.proto
├── infra/
├── setup/
│   ├── gradle.rb
│   └── README.md
├── shared/
│   └── golang/
│       └── <library_name>/
├── tools/
│   └── plugins/
│       └── <PluginName>/
├── .gitignore
├── .gitlab-ci.yml
├── .golangci.yml
├── go.mod
├── go.sum
└── README.md

Такая структура позволяет нашей команде иметь более крупные проекты, состоящие из нескольких сервисов. Мы не включаем руководящие принципы для проектов Angular, потому что они уже стандартизированы. Вместо этого у нас есть воспроизводимая структура для каждой службы на основе Go, которая состоит из основного исходного кода в cmd и управления версиями в config. Мы также используем go-kit, чтобы помочь структурировать каждый микросервис, добавляя http возможностей на транспортный уровень.

В этом дизайне MonoRepo есть еще много всего, что повысит общую продуктивность нашей команды. У нас есть setup папка, которая включает в себя формулу Gradle для установки конкретной версии, которую мы хотим, в дополнение к README.md для адаптации разработчиков. Есть также общие библиотеки Go и даже папка tools, которая содержит наши собственные плагины Gradle, написанные на Kotlin. Например, у нас есть плагины для стандартизации наших задач Go build, test и lint для разных проектов. Говоря о Gradle, мы включили сценарий сборки в каждую службу для автоматизации наших систем сборки и задач, но об этом в следующем разделе.

Наконец, у нас есть файлы конфигурации git верхнего уровня, такие как .gitignore и .gitlab-ci.yml для нашего CI / CD. На корневом уровне также есть go.mod и go.sum для управления версиями и наш верхний уровень README.md для общей документации репо. Все видно и легко переносится для упрощения рабочего процесса разработчика!

Управление сборкой Gradle + Kotlin DSL

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

Например, предположим, что у вас есть MonoRepo, который содержит комбинацию фреймворков и языков. Вместо того, чтобы запоминать клиентские команды для каждого инструмента, вы можете вместо этого создать задачу Gradle в каждом проекте. Таким образом, вы можете перейти к любому проекту и просто запустить gradle test, чтобы увидеть текущее покрытие кода этого проекта, но зачем останавливаться на этом? С таким же успехом вы можете использовать Gradle для стандартизации того, как каждый проект создается, компилируется или даже как составлять несколько контейнеров докеров с помощью одного из многих плагинов Gradle.

Более того, вы можете расширить эту стратегию для поддержания паритета с вашим конвейером CI / CD! В идеале вы можете запускать базовые задачи, такие как Gradle Test, независимо от вашей среды. Если для каждой среды вам нужны определенные конфигурации, вы можете легко отразить это в своих задачах. Возьмем, к примеру, Docker: вы можете захотеть иметь отдельные задачи dockerBuildLocal или dockerBuildPipeline, чтобы в каждой задаче были инкапсулированы определенные теги, отправка и т. Д.

Вот еще несколько отличных плагинов Gradle:

  • palantir - добавляет основные настраиваемые задачи для Docker, такие как создание образов, добавление тегов, отправка, запуск контейнеров и т. Д.
  • avast - упрощает использование Docker Compose для локальной разработки и тестирования интеграции в среде Gradle.
  • gradle-protobuf-plugin - плагин Gradle для использования буферов протокола Google в вашем проекте Gradle.
  • rest-gradle-plugin - плагин Gradle, который предоставляет инфраструктуру задач для выполнения запросов REST.
  • gradle-aws-plugin - подключаемый модуль Gradle для управления ресурсами AWS, такими как S3, EC2 и Lambda.

Если вы хотите увидеть больше потрясающих плагинов Gradle, ознакомьтесь с этим репозиторием Git:



Плюсы и минусы MonoRepo

Как и в случае с любым шаблоном проектирования, необходимо принять во внимание компромиссы, чтобы определить, подходит ли стратегия вашей команде. Во-первых, неплохо было бы с самого начала убедиться, что ваши проекты и пакеты слабо связаны. В случае с MonoRepos все еще есть недостатки, которых может быть слишком много для объединения ваших проектов в единую базу кода. Вот некоторые из основных плюсов и минусов включения MonoRepo в вашу инфраструктуру:

Плюсы:

  • Лучшая видимость и совместная работа. Все запросы на объединение вашей команды можно найти в рамках одного проекта. Кроме того, вам не нужно работать в нескольких местах для изменения функции, такой как интеграция с новым API. Вместо этого вы можете открыть MonoRepo и остаться в MonoRepo!
  • Единая база кода для многократного использования. Сохранение общих зависимостей и модулей в разных проектах - это огромный прирост производительности. Будет легче указать на запахи кода и выявить повторяющуюся логику, поскольку каждый проект будет рядом с другим.
  • Масштабный рефакторинг кода. Поскольку зависимости используются во всей кодовой базе, мы можем внести одно изменение и отразить изменения во всех пакетах, что опять-таки повысит скорость разработки.

Минусы:

  • Конвейеры CI / CD. При таком большом количестве проектов и такой большой кодовой базе может быть сложно организовать эффективную структуру сборки конвейера. Одно из первых решений - настроить вашу систему контроля версий (VCS) на запуск CI / CD только для проектов, которые были изменены или обновлены.
  • Проблемы с инструментами VCS. Говоря об управлении версиями, MonoRepo может быть сложной задачей для систем отслеживания файлов, таких как Git. Важно тщательно подумать о том, что нужно отслеживать в вашей системе, иначе столкнетесь с безумием узловых модулей.
  • Ограничения контроля доступа. Возможно, вы не захотите, чтобы каждый инженер имел доступ ко всем проектам. В этом случае вам могут потребоваться более сложные системы разрешений, если вы хотите поддерживать структуру MonoRepo для своих команд.

В целом, я считаю, что MonoRepos имеет значительные преимущества в правильных ситуациях, и поэтому стоит обратить на них внимание. Каждая команда и организация индивидуальны, поэтому, конечно же, необходимо тщательно продумать все «за» и «против», чтобы принять окончательное решение. Тем не менее, MonoRepos предлагает большие преимущества в унификации и стандартизации проектов, чтобы повысить наглядность и продуктивность команд. Что вы думаете о MonoRepos?