Монорепозиторий с Go: организация кода на практике

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

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

Начать курс

Монорепозиторий с Go: организация кода на практике

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

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

Монорепозиторий решает несколько ключевых задач:

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

Структура монорепозитория Go: базовая схема

Рассмотрим типовую структуру монорепозитория на Go. Для примера возьмем проект, состоящий из двух микросервисов (api и worker) и общей библиотеки lib:

my-monorepo/
├── go.work
├── go.work.sum
├── apps/
│   ├── api/
│   │   ├── go.mod
│   │   ├── main.go
│   │   └── handler.go
│   └── worker/
│       ├── go.mod
│       ├── main.go
│       └── process.go
├── lib/
│   ├── go.mod
│   ├── auth/
│   │   └── auth.go
│   └── db/
│       └── db.go
├── scripts/
│   ├── build.sh
│   └── test.sh
└── Makefile

Ключевые элементы:

  • go.work — файл рабочей области Go (Workspace), который связывает все модули.
  • apps/ — директория с приложениями (микросервисами, CLI-утилитами и т.д.).
  • lib/ — общие библиотеки, которые используются всеми приложениями.
  • scripts/ — вспомогательные скрипты для сборки, тестирования и деплоя.

Настройка Go Workspace (go.work)

Go 1.18+ предоставляет механизм workspace, который позволяет работать с несколькими модулями одновременно. Создадим файл go.work в корне:

go 1.22

use (
    ./apps/api
    ./apps/worker
    ./lib
)

Теперь при разработке можно запускать команды из корня проекта, и Go будет видеть все модули как единое целое. Например, чтобы запустить тесты для всех модулей:

go test ./...

А чтобы собрать конкретный сервис:

cd apps/api && go build -o bin/api .

Пример кода: общая библиотека и микросервис

Создадим простую библиотеку для аутентификации в lib/auth/auth.go:

package auth

import "errors"

var (
    ErrInvalidToken = errors.New("invalid token")
)

// ValidateToken проверяет токен и возвращает ID пользователя.
func ValidateToken(token string) (string, error) {
    if token == "" {
        return "", ErrInvalidToken
    }
    // Здесь может быть реальная проверка JWT или вызов внешнего сервиса
    return "user-123", nil
}

Теперь используем эту библиотеку в микросервисе apps/api/handler.go:

package main

import (
    "encoding/json"
    "net/http"
    "my-monorepo/lib/auth"
)

type UserResponse struct {
    UserID string `json:"user_id"`
}

func UserHandler(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("Authorization")
    userID, err := auth.ValidateToken(token)
    if err != nil {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    resp := UserResponse{UserID: userID}
    json.NewEncoder(w).Encode(resp)
}

Обратите внимание: импорт "my-monorepo/lib/auth" работает благодаря go.work. Go автоматически разрешает пути внутри рабочей области.

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

Каждый модуль имеет свой go.mod. Например, apps/api/go.mod может выглядеть так:

module my-monorepo/apps/api

go 1.22

require (
    my-monorepo/lib v0.0.0
    github.com/gorilla/mux v1.8.1
)

replace my-monorepo/lib => ../../lib

Директива replace указывает Go, что библиотека находится локально. Это стандартный подход для монорепозиториев. При использовании go.work replace-директивы можно опустить, но для CI/CD их лучше сохранить.

Для автоматического обновления всех модулей используйте команду:

go work sync

Она синхронизирует версии зависимостей между всеми модулями в рабочей области.

CI/CD в монорепозитории Go

Монорепозиторий позволяет эффективно настроить пайплайны. Пример скрипта scripts/build.sh:

#!/bin/bash
set -e

# Определяем, какие модули изменились
CHANGED=$(git diff --name-only HEAD~1 | grep '^apps/' | cut -d'/' -f2 | sort -u)

for app in $CHANGED; do
    echo "Building $app..."
    cd apps/$app
    go build -o bin/$app .
    cd ../..
done

Этот скрипт собирает только те приложения, которые были изменены в последнем коммите. В реальном проекте можно добавить параллельную сборку, кэширование и отправку артефактов в реестр.

Лучшие практики организации монорепозитория Go

  • Используйте единую версию Go — укажите одинаковую версию в go.mod всех модулей и в

Блоги

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