【Go】gorm × Atlasで始めるマイグレーションのバージョン管理

# はじめに
こんにちは、H×Hのセンリツ大好きエンジニアです。（同担OKです😉）

今回はGoのマイグレーションツール**Atlas**の始め方について記事を書きました！
（記事キャンペーンに投稿してQiitaオリジナルグッズが欲しいのは内緒🤫）

背景として、gormをORMに使用していたのですが、マイグレーションをバージョン管理したいな。。。という願望がありましたのでAtlasを始めました！

# Atlasとは？
公式サイト：[atlas](https://atlasgo.io/)

Atlasは、データベースのマイグレーションツールになります。
バージョンマイグレーションと、宣言的マイグレーションの2通りが使用できる＆多機能になっているのでかなりオススメです！

- バージョンマイグレーション：マイグレーションをバージョン管理する
- 宣言的マイグレーション：現状のスキーマファイルとDBを比較して内部的に変更を検知した上でマイグレーションが実行される

様々な言語のORMに対応していますが、今回はgormでバージョンマイグレーションを行います。
また、MySQLを始めとした様々なデータベースをサポートしてます。

# Atlasのセットアップ
MacOSとLinuxの場合は以下のコマンドでインストールできます
``` zsh
curl -sSf https://atlasgo.sh | sh
```

次に、`atlas.hcl`というファイルを作成します。
こちらのファイルは、DBスキーマを宣言するために使用されます。

※この方法はgorm.Modelが同一パッケージ内に全て存在している場合のみ使用可能です
自分の場合は、`domain/models`に全てのモデルを宣言しているので使用してます。

``` atlas.hcl
data "external_schema" "gorm" {
  program = [
    "go",
    "run",
    "-mod=mod",
    "ariga.io/atlas-provider-gorm",
    "load",
    "--path", "./domain/models",
    "--dialect", "mysql", // | postgres | sqlite | sqlserver
  ]
}

env "gorm" {
  src = data.external_schema.gorm.url
  dev = "docker://mysql/8/dev"
  migration {
    dir = "file://migrations"
  }
  format {
    migrate {
      diff = "{{ sql . \"  \" }}"
    }
  }
}
```

次に、gorm.Modelsを用意します。
``` models.go
package models

import "gorm.io/gorm"

type User struct {  
    gorm.Model
    Name string
    Pets []Pet
}

type Pet struct {
    gorm.Model
    Name   string
    User   User
    UserID uint
}
```
# Atlasでのバージョンマイグレーションと適用
この構造体から、マイグレーションファイルを作成します。
Atlasでは、以下のコマンドでマイグレーションファイルが生成されます。
``` zsh
atlas migrate diff --config file://atlas.hcl --env gorm 
```

すると、`migrations`ディレクトリが作成され、中にマイグレーションのバージョンファイルとそのハッシュ値を管理するファイルが作成されます。

```
migrations
|-- 20230627123246.sql <- バージョン管理されたマイグレーションファイル
`-- atlas.sum <- 勝手に変更出来ないように、ハッシュ値で管理されている

0 directories, 2 files
```

これのありがたい所が、自分の手で勝手にマイグレーションファイルを書き換えてしまってもハッシュ値が異なれば`atlas`コマンドが動作しなくなるため、誤作動と改ざんを防いでくれます。😉

また、マイグレーションファイルの中身を覗くと以下のように、きちんと構造体からSQL分を生成してくれてることが分かりますね。
``` 20230627123246.sql
-- Create "users" table
CREATE TABLE `users` (
 `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 `created_at` datetime(3) NULL,
 `updated_at` datetime(3) NULL,
 `deleted_at` datetime(3) NULL,
 `name` longtext NULL,
 PRIMARY KEY (`id`),
 INDEX `idx_users_deleted_at` (`deleted_at`)
) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- Create "pets" table
CREATE TABLE `pets` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `created_at` datetime(3) NULL,
  `updated_at` datetime(3) NULL,
  `deleted_at` datetime(3) NULL,
  `name` longtext NULL,
  `user_id` bigint unsigned NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_users_pets` (`user_id`),
  INDEX `idx_pets_deleted_at` (`deleted_at`),
  CONSTRAINT `fk_users_pets` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
```

後は、生成されたマイグレーションファイルをDBに適用するだけなので、以下のコマンドで実行します。
※公式ではAtlas Cloudを使用していますが、わざわざGUIで確認しに行くのも手間なので、CLIで完結させます
``` zsh
atlas migrate apply --url "自身のMySQLパス"
```

これでDBを見てみると、きちんと追加されてますね🫣

``` zsh
mysql> show tables;
+--------------------+
| Tables_in_database |
+--------------------+
| users              |
| pets               |
+--------------------+
2 rows in set (0.01 sec)
```

# おわりに
gormとAtlasを使用してマイグレーションのバージョン管理を行う方法をご紹介しました！
かなりニッチな内容なので、誰得かは分かりませんが役に立てることを願っております🥹

気になったこと（ご感想でも🥴）などございましたら、是非コメントしていただけると嬉しいです！

最後までご覧いただきありがとうございました！センリツでした🤓