go-migrationgo-migration
Getting Started
Documentation

Project Structure

Recommended directory layout for projects using go-migration.

Project Structure

go-migration doesn't enforce a specific directory layout, but the following structure works well for most projects.

my-project/
├── cmd/
│   └── migrator/
│       └── main.go             # CLI entry point (generated by go-migration init)
├── database/
│   ├── migrations/
│   │   ├── 2025_01_15_143022_1234_create_users_table.go
│   │   ├── 2025_01_15_143025_5678_create_posts_table.go
│   │   └── 2025_02_01_091500_2345_add_avatar_to_users.go
│   ├── seeders/
│   │   ├── user_seeder.go
│   │   ├── post_seeder.go
│   │   └── registry.go             # Central seeder registration
│   └── factories/
│       ├── user_factory.go
│       └── post_factory.go
├── go.mod
├── go.sum
├── migration.json              # Database configuration (JSON format)

Migration Files

Each migration lives in its own file inside the database/migrations/ directory. Files use the naming format YYYY_MM_DD_HHMMSS_RRRR_description.go, where RRRR is a random 4-digit number to prevent timestamp collisions:

database/migrations/
├── 2025_01_15_143022_1234_create_users_table.go
├── 2025_01_15_143025_5678_create_posts_table.go
└── 2025_02_01_091500_2345_add_avatar_to_users.go

The old naming format (YYYYMMDDHHMMSS_description.go) is still supported for backward compatibility. You can mix both formats in the same project.

Each file defines a single struct implementing the migration interface, with an init() function for auto-registration:

database/migrations/2025_01_15_143022_1234_create_users_table.go
package migrations

import (
    "github.com/gopackx/go-migration/migrator"
    "github.com/gopackx/go-migration/schema"
)

func init() {
    migrator.AutoRegister("2025_01_15_143022_1234_create_users_table", &CreateUsersTable{})
}

type CreateUsersTable struct{}

func (m *CreateUsersTable) Up(s *schema.Builder) error {
    return s.Create("users", func(bp *schema.Blueprint) {
        bp.ID()
        bp.String("name", 255)
        bp.String("email", 255).Unique()
        bp.Timestamps()
    })
}

func (m *CreateUsersTable) Down(s *schema.Builder) error {
    return s.Drop("users")
}

Auto-Discovery Setup

With auto-discovery, you don't need a registry.go file. Each migration registers itself via init(), and the migrator loads them with a blank import. The recommended approach (generated by go-migration init) uses migrator.Run():

cmd/migrator/main.go
package main

import (
    "github.com/gopackx/go-migration/migrator"
    _ "your-module/database/migrations"
    _ "your-module/database/seeders"
    _ "github.com/lib/pq" // PostgreSQL driver
)

func main() {
    migrator.Run()
}

Auto-discovery eliminates the need for a central registration file. Adding a new migration is as simple as creating a new file with an init() function — no other files need to change.

Seeder Files

Seeders follow the same pattern — one file per seeder, with a registry for central registration:

database/seeders/user_seeder.go
package seeders

import "database/sql"

type UserSeeder struct{}

func (s *UserSeeder) Run(db *sql.DB) error {
    _, err := db.Exec(`INSERT INTO users (name, email) VALUES ($1, $2)`, "Alice", "alice@example.com")
    return err
}

Factory Files

Factories define how to generate test data for your models:

database/factories/user_factory.go
package factories

import "github.com/gopackx/go-migration/seeder/factory"

type User struct {
    ID    int
    Name  string
    Email string
}

func NewUserFactory() *factory.Factory[User] {
    return factory.NewFactory(func(f factory.Faker) User {
        return User{
            Name:  f.Name(),
            Email: f.Email(),
        }
    })
}

This layout is a recommendation, not a requirement. go-migration works with any directory structure — organize your files in whatever way makes sense for your project.

What's Next?

  • Migrations — learn how to define and manage migrations
  • Seeders — populate your database with test data
  • Factories — generate realistic test data with factories