Монорепозиторий с Go: организация кода для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое монорепозиторий и зачем он нужен в Go?

Монорепозиторий (monorepo) — это подход к управлению кодом, при котором все проекты, библиотеки и сервисы хранятся в одном репозитории. В экосистеме Go этот подход становится особенно популярным благодаря встроенной поддержке модулей и workspace'ов.

Для начинающих разработчиков монорепозиторий даёт несколько ключевых преимуществ:

  • Единая версия кода — все зависимости синхронизированы
  • Упрощённый рефакторинг — изменения сразу видны во всех проектах
  • Стандартизация — одинаковые настройки линтеров, CI/CD и тестов
  • Прозрачность — легко понять, как связаны разные части системы

В этой статье мы разберём, как правильно организовать монорепозиторий на Go, начиная с базовой структуры и заканчивая практическими примерами.

Базовая структура монорепозитория на Go

Правильная организация кода — основа успешного монорепозитория. Рассмотрим типовую структуру:

my-monorepo/
├── apps/
│   ├── web-api/          # Основное приложение
│   │   ├── main.go
│   │   ├── go.mod
│   │   └── internal/
│   └── worker/           # Фоновый обработчик
│       ├── main.go
│       └── go.mod
├── libs/
│   ├── config/           # Общая конфигурация
│   │   ├── config.go
│   │   └── go.mod
│   └── logger/           # Логирование
│       ├── logger.go
│       └── go.mod
├── scripts/              # Вспомогательные скрипты
├── docs/                 # Документация
├── go.work               # Go workspace
└── README.md

Ключевые принципы этой структуры:

  • apps/ — исполняемые приложения (бинарники)
  • libs/ — переиспользуемые библиотеки
  • scripts/ — автоматизация и утилиты
  • docs/ — документация проекта

Настройка Go Workspace для монорепозитория

Начиная с Go 1.18, появилась официальная поддержка workspace'ов через файл go.work. Это позволяет работать с несколькими модулями одновременно без необходимости публиковать их в удалённый репозиторий.

Создадим workspace для нашего монорепозитория:

// go.work
go 1.22

use (
    ./apps/web-api
    ./apps/worker
    ./libs/config
    ./libs/logger
)

После создания go.work все модули из списка use будут доступны друг другу локально. Это особенно удобно при разработке — изменения в библиотеке сразу видны в приложениях.

Инициализация модулей

Для каждого компонента нужно создать свой go.mod. Пример для библиотеки конфигурации:

// libs/config/go.mod
module github.com/your-org/libs/config

go 1.22

И для приложения web-api:

// apps/web-api/go.mod
module github.com/your-org/apps/web-api

go 1.22

require github.com/your-org/libs/config v0.0.0

replace github.com/your-org/libs/config => ../../libs/config

Обратите внимание на replace — он указывает Go использовать локальную версию библиотеки, а не искать её в интернете.

Пример кода: библиотека и приложение

Создадим простую библиотеку для конфигурации и приложение, которое её использует.

Библиотека libs/config

// libs/config/config.go
package config

import (
    "fmt"
    "os"
)

type Config struct {
    Port string
    Env  string
}

func Load() (*Config, error) {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    env := os.Getenv("ENV")
    if env == "" {
        env = "development"
    }

    return &Config{
        Port: port,
        Env:  env,
    }, nil
}

func (c *Config) String() string {
    return fmt.Sprintf("Config{Port: %s, Env: %s}", c.Port, c.Env)
}

Приложение apps/web-api

// apps/web-api/main.go
package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/your-org/libs/config"
)

func main() {
    cfg, err := config.Load()
    if err != nil {
        log.Fatalf("Failed to load config: %v", err)
    }

    fmt.Printf("Starting server with config: %s\n", cfg)

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from %s environment!", cfg.Env)
    })

    addr := fmt.Sprintf(":%s", cfg.Port)
    log.Printf("Server listening on %s", addr)
    log.Fatal(http.ListenAndServe(addr, nil))
}

Теперь, запустив go run ./apps/web-api из корня монорепозитория, вы получите работающий HTTP-сервер, использующий локальную библиотеку конфигурации.

Управление зависимостями и версиями

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

Рекомендации по работе с зависимостями:

  • Минимизируйте внешние зависимости — используйте стандартную библиотеку Go, где это возможно
  • Фиксируйте версии — в go.sum хранятся контрольные суммы всех зависимостей
  • Обновляйте централизованно — изменения в libs/ сразу доступны всем приложениям
  • Используйте CI/CD — автоматическая проверка всех модулей при каждом коммите

Заключение

Монорепозиторий на Go — это мощный инструмент для организации кода, особенно когда проекты разрастаются и появляется несколько сервисов или библиотек. Go Workspace делает работу с монорепозиторием простой и инту

Блоги

Рекомендаций по книгам