ent の migration に atlas を使ってみた所感や、そもそも atlas ってどんなものかということを紹介しています。 OSS のデータアクセスフレームワーク。Goコードで任意のデータモデルまたはグラフ構造を簡単に定義でき、データアクセス周りの CRUD 処理などが自動生成可能。 OSS のデータベーススキーマ管理ツール。CLI と GUI が提供されている。 特徴 docs や blog にも最新の情報があるのでご覧になってみてください。特に blog は docs よりも思想の部分に多く触れているので面白かったです。 schema 元のファイル生成 schema 定義追加(Field 定義) モデル操作など関連ファイルの生成を実行。 初回 migrate 実行。ここで初めて users テーブルが作られます。 以降、atlas 使った migration お試し。getting start のレールからは外れます。 install@Mac atlas で現在の schema を確認してみます atlas は GUI でも schema 確認可能なので見ておきましょう こんな画面が立ち上り、users テーブルの存在が確認できました。 現状を GUI で簡単に確認できるのは便利ですよね その前準備として、 その後、 このメソッドを使って、ent 配下の schema と現在の DB の状態を比較することができるようになります。
以下を実行すると、migration file が生成できます。 差分を作りたいので、新しいテーブルを追加してみます。 追加した定義を schema に反映させます。 実行後は、 先程の main 関数での migrate 処理はここを見に行くようです table 定義と対応した go file から sql 文が生成できるのは便利ですね ただし、atlas 自体には migrate の実行管理する機能が現時点でないので実行管理まで行いたい場合は、別のツールを使う必要があります。対応予定はあるようです。 ent を運用していく際の schema 変更を atlas で運用するとどんな感じなのか実際に動かして試してみました。 ent に atlas が組み込まれるようになったのは 最近 なので、ent の機能拡張とともにさらなる進化が見込めそうです。version 管理は早く提供してほしいですよね。(GUI 操作で見る限りオンメモリでは持てるようになっているのでもうすぐかも)今後も watch してまたブログでコメントしていきます。
このページについて
そもそも ent / atlas ってなに
ent
本記事では深く踏み込まないのでこのくらいの紹介にさせてください。atlas
&schema.Table{
Name: "users",
Schema: &schema.Schema{(CYCLIC REFERENCE)},
Columns: {
&schema.Column{
Name: "id",
Type: &schema.ColumnType{
Type: &schema.IntegerType{
T: "int",
Unsigned: false,
},
Null: false,
},
},
},
PrimaryKey: &schema.Index{
Unique: false,
Table: &schema.Table{(CYCLIC REFERENCE)},
Attrs: nil,
Parts: {
&schema.IndexPart{
SeqNo: 0,
Desc: false,
C: &schema.Column{(CYCLIC REFERENCE)},
},
},
},
}
実行例
前提
$ atlas version
atlas CLI version v0.3.5-f8eaeba27107-canary
https://github.com/ariga/atlas/releases/latest
$ cat go.mod | grep ent
module github.com/smith-30/goparco/ent_playground
entgo.io/ent v0.10.1
初期 schema セットアップ
$ ent init User
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.String("name").
Default("unknown"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return nil
}
$ go generate ./ent
package main
import (
"context"
"log"
_ "github.com/mattn/go-sqlite3"
"github.com/smith-30/goparco/ent_playground/ent"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent.db?_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
atlas を利用して schema 変更管理
$ brew install ariga/tap/atlas
HCL で表示されるようになってます。(SQL を表示する機能は v0.3.5-f8eaeba27107-canary 時点ではありません)
SQL を見たい場合は dbmate あたり使うと簡単に出力できるのでおすすめです。$ atlas schema inspect -d "sqlite://file:ent.db?_fk=1"
table "users" {
schema = schema.main
column "age" {
null = false
type = integer
}
column "name" {
null = false
type = varchar(255)
default = "unknown"
}
column "id" {
null = false
type = integer
auto_increment = true
}
primary_key {
columns = [column.id]
}
}
schema "main" {
}
$ atlas schema inspect -d "sqlite://file:ent.db?_fk=1" -w
Atlas UI available at: http://127.0.0.1:5800/projects/30064771073/schemas/1
Press Ctrl+C to stop
前述のとおり、schema を Go 構造体で読み込んで API として返しているので Relation があれば表示するようになってます。
今度は schema 変更をして、migration を実行してみたいと思います。
こちらの tutorial を参考にしています。./ent/generate.go
を下記のように変更します。
--feature sql/versioned-migration
のフラグの追加です。package ent
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema
$ go generate ./ent
で追加したフラグ分の自動生成を反映させます。
実行後は、./ent/migrate/migrate.go
にメソッドが追加されます// Diff creates a migration file containing the statements to resolve the diff
// between the Ent schema and the connected database.
func (s *Schema) Diff(ctx context.Context, opts ...schema.MigrateOption) error {
migrate, err := schema.NewMigrate(s.drv, opts...)
if err != nil {
return fmt.Errorf("ent/migrate: %w", err)
}
return migrate.Diff(ctx, Tables...)
}
package main
import (
"context"
"log"
"ariga.io/atlas/sql/migrate"
"entgo.io/ent/dialect/sql/schema"
_ "github.com/mattn/go-sqlite3"
"github.com/smith-30/goparco/ent_playground/ent"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent.db?_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
//
// migration
//
ctx := context.Background()
// Create a local migration directory.
dir, err := migrate.NewLocalDir("migrations")
if err != nil {
log.Fatalf("failed creating atlas migration directory: %v", err)
}
// Write migration diff.
err = client.Schema.Diff(ctx, schema.WithDir(dir))
if err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
$ go run entgo.io/ent/cmd/ent init Car
ent/schema/car.go
にフィールド定義を追加します。
// Fields of the Car.
func (Car) Fields() []ent.Field {
return []ent.Field{
field.String("model"),
field.Time("registered_at"),
}
}
$ go generate ./ent
ent/migrate/schema.go
が更新されます
main.go を実行すると migration file が生成されます{timestamp}.up.sql
CREATE TABLE `cars` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `model` text NOT NULL, `registered_at` datetime NOT NULL);
{timestamp}.down.sql
DROP TABLE `cars`;
普段は追加・変更したい sql を書いて実行してコードを変更するというフローでしたが、その手間が自動化されてよりコードに集中できるようになったと思います
この生成方法だと特定機能毎に migration file はできるかと思うので、他のツール使わないで手動実行するときの運用方法はうまく考えないといけないかなと思います。
リリース頻度が少なく、実行する migration file が多い場合はおとなしくツールに sql 実行を任せるのがよさそうです。まとめ
既存の project で利用している migration tool から乗り換えるにはまだ検討の余地があるかと思います。
しかし、新規 project で ent を使ってみるとなった場合は素直に使ってみてもいいのかなと思いました(ent の自動化のフローに乗っかれるため)。