Какие инструменты для управления миграциями баз данных вы использовали в Go-проектах и почему?

Ответ

Да, я регулярно работал с миграциями баз данных. В Go-проектах наиболее популярны два подхода: использование .sql файлов и написание миграций на самом Go.

Я использовал следующие инструменты:

  1. golang-migrate/migrate: Это, пожалуй, самая популярная и универсальная библиотека. Она поддерживает огромное количество баз данных и источников миграций (файлы, go-bindata, S3 и т.д.).

    • Подход: Используются .sql файлы с определенным форматом именования, например: 000001_create_users_table.up.sql и 000001_create_users_table.down.sql.
    • Пример ...up.sql файла:
      CREATE TABLE users (
          id UUID PRIMARY KEY,
          name VARCHAR(100) NOT NULL,
          email VARCHAR(100) UNIQUE NOT NULL,
          created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
      );
  2. pressly/goose: Еще один популярный инструмент, который также делает упор на .sql файлы, но с другим синтаксисом внутри файла.

    • Подход: Используются комментарии -- +goose Up и -- +goose Down в одном SQL-файле.
    • Пример файла миграции:

      -- +goose Up
      CREATE TABLE products (
          id SERIAL PRIMARY KEY,
          name TEXT NOT NULL,
          price NUMERIC(10, 2) NOT NULL
      );
      
      -- +goose Down
      DROP TABLE products;
  3. Программные миграции: Иногда, если логика миграции сложна (например, требует переноса и трансформации данных), миграции пишутся на Go. Это можно сделать с помощью того же golang-migrate/migrate или других библиотек.

    • Пример запуска миграций из кода:

      import "github.com/golang-migrate/migrate/v4"
      // ... импорты драйверов БД и источников
      
      func RunMigrations(databaseURL, migrationsPath string) error {
          m, err := migrate.New(
              "file://" + migrationsPath, // источник миграций
              databaseURL,               // URL подключения к БД
          )
          if err != nil {
              return err
          }
          // Применяем все доступные миграции "вверх"
          if err := m.Up(); err != nil && err != migrate.ErrNoChange {
              return err
          }
          return nil
      }

Выбор инструмента зависит от проекта. Для большинства CRUD-сервисов я предпочитаю golang-migrate/migrate с SQL-файлами за его простоту, наглядность и независимость от кода приложения. Программные миграции использую в редких случаях, когда требуется сложная логика преобразования данных.