How to Implement the Repository Pattern with database/sql

Implement the Repository Pattern in Go by defining an interface for data access and a concrete struct using database/sql to handle queries.

Implement the Repository Pattern by defining an interface for data operations and a concrete struct that uses database/sql to execute queries.

// Define the interface
//go:generate mockgen -source=repository.go -destination=mock_repository.go -package=mocks

type UserRepository interface {
    GetByID(ctx context.Context, id int) (*User, error)
    Save(ctx context.Context, user *User) error
}

// Define the concrete implementation
type userRepository struct {
    db *sql.DB
}

func NewUserRepository(db *sql.DB) UserRepository {
    return &userRepository{db: db}
}

func (r *userRepository) GetByID(ctx context.Context, id int) (*User, error) {
    var user User
    err := r.db.QueryRowContext(ctx, "SELECT id, name, email FROM users WHERE id = $1", id).
        Scan(&user.ID, &user.Name, &user.Email)
    if err == sql.ErrNoRows {
        return nil, nil
    }
    return &user, err
}

func (r *userRepository) Save(ctx context.Context, user *User) error {
    _, err := r.db.ExecContext(ctx, "INSERT INTO users (name, email) VALUES ($1, $2)", user.Name, user.Email)
    return err
}