Implementing the repository pattern in Go with both in-memory and MySQL repositories
Tags: go, mysql, repository, pattern
Published: 2024-04-25 03:37:55

1. - We define the User struct representing a user entity.
2. - We define the UserRepository interface with methods for managing users.
3. - We implement InMemoryUserRepository and MySQLUserRepository to provide in-memory and MySQL-based repositories, respectively.
4. - NewInMemoryUserRepository and NewMySQLUserRepository are constructor functions for creating instances of the respective repositories.
5. - We demonstrate how to use both repositories in the main function by inserting users, getting a user by ID, and getting all users.

```golang
package main

import (
	"database/sql"
	"errors"
	"fmt"

	_ "github.com/go-sql-driver/mysql"
)

// User represents a user entity
type User struct {
	ID       int
	Username string
	Email    string
}

// UserRepository defines the methods a user repository must implement
type UserRepository interface {
	Insert(user *User) error
	GetByID(id int) (*User, error)
	GetAll() ([]*User, error)
}

// InMemoryUserRepository is an in-memory implementation of UserRepository
type InMemoryUserRepository struct {
	users []*User
}

// NewInMemoryUserRepository creates a new instance of InMemoryUserRepository
func NewInMemoryUserRepository() *InMemoryUserRepository {
	return &InMemoryUserRepository{
		users: make([]*User, 0),
	}
}

// Insert inserts a new user into the repository
func (repo *InMemoryUserRepository) Insert(user *User) error {
	repo.users = append(repo.users, user)
	return nil
}

// GetByID retrieves a user by its ID from the repository
func (repo *InMemoryUserRepository) GetByID(id int) (*User, error) {
	for _, user := range repo.users {
		if user.ID == id {
			return user, nil
		}
	}
	return nil, errors.New("user not found")
}

// GetAll retrieves all users from the repository
func (repo *InMemoryUserRepository) GetAll() ([]*User, error) {
	return repo.users, nil
}

// MySQLUserRepository is a MySQL implementation of UserRepository
type MySQLUserRepository struct {
	db *sql.DB
}

// NewMySQLUserRepository creates a new instance of MySQLUserRepository
func NewMySQLUserRepository(dataSourceName string) (*MySQLUserRepository, error) {
	db, err := sql.Open("mysql", dataSourceName)
	if err != nil {
		return nil, err
	}
	return &MySQLUserRepository{
		db: db,
	}, nil
}

// Insert inserts a new user into the MySQL repository
func (repo *MySQLUserRepository) Insert(user *User) error {
	_, err := repo.db.Exec("INSERT INTO users (id, username, email) VALUES (?, ?, ?)", user.ID, user.Username, user.Email)
	if err != nil {
		return err
	}
	return nil
}

// GetByID retrieves a user by its ID from the MySQL repository
func (repo *MySQLUserRepository) GetByID(id int) (*User, error) {
	row := repo.db.QueryRow("SELECT id, username, email FROM users WHERE id = ?", id)
	user := &User{}
	err := row.Scan(&user.ID, &user.Username, &user.Email)
	if err != nil {
		return nil, err
	}
	return user, nil
}

// GetAll retrieves all users from the MySQL repository
func (repo *MySQLUserRepository) GetAll() ([]*User, error) {
	rows, err := repo.db.Query("SELECT id, username, email FROM users")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []*User
	for rows.Next() {
		user := &User{}
		err := rows.Scan(&user.ID, &user.Username, &user.Email)
		if err != nil {
			return nil, err
		}
		users = append(users, user)
	}
	return users, nil
}

func main() {
	// Example usage of in-memory repository
	memRepo := NewInMemoryUserRepository()
	memRepo.Insert(&User{ID: 1, Username: "user1", Email: "user1@example.com"})
	memRepo.Insert(&User{ID: 2, Username: "user2", Email: "user2@example.com"})

	user, err := memRepo.GetByID(1)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("In-Memory User:", user)
	}

	allUsers, err := memRepo.GetAll()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("In-Memory All Users:", allUsers)
	}

	// Example usage of MySQL repository
	mysqlRepo, err := NewMySQLUserRepository("user:password@tcp(127.0.0.1:3306)/database_name")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	mysqlRepo.Insert(&User{ID: 3, Username: "user3", Email: "user3@example.com"})
	mysqlRepo.Insert(&User{ID: 4, Username: "user4", Email: "user4@example.com"})

	user, err = mysqlRepo.GetByID(3)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("MySQL User:", user)
	}

	allUsers, err = mysqlRepo.GetAll()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("MySQL All Users:", allUsers)
	}
}

```