😽

Ent ORM: スキーマ定義と生成コードの分離術

に公開

この記事では、Go の Ent ORM において以下のディレクトリを分離するカスタムセットアップについて説明します。

  • スキーマ定義(開発者が作成するもの)
  • 生成コード(Entによって作成される)

Ent ORMとは?

Entは、Meta(旧Facebook)によって開発・保守されている、Schema First な ORM です。スキーマ定義に基づいて型安全なコードを生成してくれます。

なぜカスタムセットアップが必要なのか?

デフォルトのEntセットアップでは、スキーマ定義と生成コードが同じディレクトリに配置されるため、以下の問題が生じます:

  1. 手書きコードと自動生成コードが混在し、ディレクトリが煩雑になる
  2. 生成ファイルを誤って変更してしまう可能性
  3. どのファイルが変更可能かの判断が困難

以下の例(Ent の Getting Started 通りにした場合)では、schema/*(スキーマ定義)は開発者が作成する一方、それ以外のファイルは Ent によって生成されます(全て ent/ 配下に混在)。

ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... truncated
├── schema
│   └── user.go
├── tx.go
├── user
│   ├── user.go
│   └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go

以降のカスタムセットアップは、スキーマ定義と生成コードを分離することで、これらの問題を解決します。

カスタムセットアップの手順

1. ディレクトリ構造

まず、ディレクトリ構造を設定します。以下は一例で、プロジェクトの構成に応じて適宜調整してください:

infrastructure/postgres/  # 例:データベース関連のコード
├── ent/                  # 
│   ├── schema/           # スキーマ定義(**開発者が作成するもの**)
│   └── generate.go       # コード生成ディレクティブ
├── entgen/               # 生成コード(**Entによって作成される**)
└── ...

注意: infrastructure/postgres/の部分は一例です。プロジェクトの構造に合わせて、適切なパスに置き換えてください。重要なのはent/entgen/の相対的な配置です。

2. スキーマ定義ファイル

infrastructure/postgres/ent/schema/にスキーマ定義ファイルを作成します。以下はシンプルなUserエンティティの例です:

infrastructure/postgres/ent/schema/user.go
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.String("name").NotEmpty(),
        field.String("email").Unique(),
        field.Int("age").Positive(),
    }
}

このファイルは 開発者が作成・管理するファイル です。一方、この定義から生成されるentgen/配下のファイル群は Entが自動生成するファイル となり、手動で編集してはいけません。

3. コード生成設定

infrastructure/postgres/ent/ディレクトリに以下の内容のgenerate.goファイルを作成します:

infrastructure/postgres/ent/generate.go
package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --target ../entgen ./schema

このディレクティブは、Entに以下を指示します:

  • ./schemaのスキーマファイルを使用してコードを生成
  • 生成コードを../entgenに出力(entディレクトリからの相対パス)
  • 生成コードのパッケージ名としてentgenを使用(デフォルトのentの代わり)

4. Makefileの統合(オプション)

コード生成のための便利なMakefileターゲットを追加:

Makefile
.PHONY: gen.ent
gen.ent:
    cd infrastructure/postgres/ent && go generate ./...

5. コードの生成

スキーマを変更した後、Entコードを生成するには:

make gen.ent

または直接生成コマンドを実行:

cd infrastructure/postgres/ent && go generate ./...

これにより以下が実行されます:

  1. ./schemaからスキーマファイルを読み取り
  2. generate.goの設定を使用してコードを生成
  3. 生成コードを../entgenに出力

6. 生成コードの使用

生成後、entgenディレクトリには以下を含むすべての生成コードが格納されます:

  • エンティティモデル
  • クエリビルダー
  • クライアントコード

アプリケーションで生成コードを使用する例:

func ExampleUsage() {
    client, err := entgen.Open("postgres", "your-connection-string")
    if err != nil {
        panic(err)
    }
    defer client.Close()

    // クライアントを使用してエンティティをクエリまたは作成
    ctx := context.Background()
    car, err := client.Car.Create().
        SetModel("some-car-model").
        SetTenantID("some-tenant-id").
        Save(ctx)

    if err != nil {
        panic(err)
    }

    // 作成されたcarを使用
    _ = car
}

このカスタムセットアップのメリット

明確な分離: 手書きコードと自動生成コードが別ディレクトリに分かれることで、どのファイルを変更すべきかが一目瞭然になり、誤って生成ファイルを編集してしまうリスクを防げます。また、リントルールをどこに適用すべきかも明確になります。

Discussion