Repositories
Introduction
Repositories are part of the Data layer. They implement the methods that will be called to work with the data (fetch, create, update, delete, etc) and will use models. Models are a Go representation of your database schema. An instance of a model is a single database record.
A repository should not have any dependency to a service, but can depend on other repositories. Therefore, it must interact only with the database: its purpose is to execute the necessary unitary database operation for data manipulation. No business logic should be implemented in the repositories.
For example, if we have a system that tracks user actions (user history) and we want a "register" history entry to be created when the user creates their account, we would have two repositories that don't depend on each other:
- the
user.Create
method should only create the user, not the associated history entry. - the
history.Create
method should only create the history entry.
It is the services job to handle these kind of scenarios, using a transaction.
Models
Models are defined in the database/model
package. Each resource has its own file. Models are usually just normal Golang structs, basic Go types, or pointers of them. sql.Scanner
and driver.Valuer
interfaces are also supported.
Example:
// database/model/user.go
package model
import (
"time"
"gopkg.in/guregu/null.v4"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt null.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Email string `gorm:"uniqueIndex"`
}
func (User) TableName() string {
return "users"
}
INFO
It is recommended to always define a TableName()
method.
TIP
Learn more about model definition on Gorm's documentation.
Implementation
Each repository should have its own file in database/repository
, named after the resource it is using, in singular form.
Full example:
// database/repository/user.go
package repository
import (
"context"
"gorm.io/gorm"
"goyave.dev/goyave/v5/database"
"goyave.dev/goyave/v5/util/errors"
"goyave.dev/template/database/model"
)
// User repository for user manipulation in the database.
type User struct {
DB *gorm.DB
}
// NewUser create a new user repository.
func NewUser(db *gorm.DB) *User {
return &User{
DB: db,
}
}
// Paginate returns a paginator after executing it.
func (r *User) Paginate(ctx context.Context, page int, pageSize int) (*database.Paginator[*model.User], error) {
users := []*model.User{}
paginator := database.NewPaginator(r.DB, page, pageSize, &users)
err := paginator.Find()
return paginator, err
}
// First returns the user identified by the given ID, or `nil`
func (r *User) First(ctx context.Context, id uint) (*model.User, error) {
var user *model.User
db := r.DB.Where("id", id).First(&user)
return user, errors.New(db.Error)
}