go-repository
4
总安装量
4
周安装量
#51579
全站排名
安装命令
npx skills add https://github.com/cristiano-pacheco/ai-rules --skill go-repository
Agent 安装分布
trae
4
gemini-cli
4
antigravity
4
codebuddy
4
claude-code
4
github-copilot
4
Skill 文档
Go Repository
Generate repository port interfaces and implementations for Go modular architecture conventions.
Two-File Pattern
Every repository requires two files:
- Port interface:
internal/modules/<module>/ports/<entity>_repository.go - Repository implementation:
internal/modules/<module>/repository/<entity>_repository.go
Port Interface Structure
Location: internal/modules/<module>/ports/<entity>_repository.go
package ports
import (
"context"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/model"
)
type EntityRepository interface {
FindAll(ctx context.Context) ([]model.EntityModel, error)
FindByID(ctx context.Context, id uint64) (model.EntityModel, error)
Create(ctx context.Context, entity model.EntityModel) (model.EntityModel, error)
Update(ctx context.Context, entity model.EntityModel) (model.EntityModel, error)
Delete(ctx context.Context, id uint64) error
}
Pagination variant:
FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error)
Custom methods: Add domain-specific queries as needed (e.g., FindByName, AssignContacts).
Repository Implementation Structure
Location: internal/modules/<module>/repository/<entity>_repository.go
package repository
import (
"context"
"errors"
"github.com/cristiano-pacheco/bricks/pkg/errs"
"github.com/cristiano-pacheco/bricks/pkg/otel/trace"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/model"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/ports"
"github.com/cristiano-pacheco/pingo/internal/shared/database"
"gorm.io/gorm"
)
type EntityRepository struct {
*database.PingoDB
}
var _ ports.EntityRepository = (*EntityRepository)(nil)
func NewEntityRepository(db *database.PingoDB) *EntityRepository {
return &EntityRepository{db}
}
Method Implementations
FindAll (Simple)
func (r *EntityRepository) FindAll(ctx context.Context) ([]model.EntityModel, error) {
ctx, span := trace.Span(ctx, "EntityRepository.FindAll")
defer span.End()
entities, err := gorm.G[model.EntityModel](r.DB).Find(ctx)
if err != nil {
return nil, err
}
return entities, nil
}
FindAll (Paginated)
func (r *EntityRepository) FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error) {
ctx, span := trace.Span(ctx, "EntityRepository.FindAll")
defer span.End()
offset := (page - 1) * pageSize
var total int64
if err := r.DB.Model(&model.EntityModel{}).Count(&total).Error; err != nil {
return nil, 0, err
}
entities, err := gorm.G[model.EntityModel](r.DB).
Limit(pageSize).
Offset(offset).
Find(ctx)
if err != nil {
return nil, 0, err
}
return entities, total, nil
}
FindByID
func (r *EntityRepository) FindByID(ctx context.Context, id uint64) (model.EntityModel, error) {
ctx, span := trace.Span(ctx, "EntityRepository.FindByID")
defer span.End()
entity, err := gorm.G[model.EntityModel](r.DB).
Where("id = ?", id).
Limit(1).
First(ctx)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return model.EntityModel{}, errs.ErrRecordNotFound
}
return model.EntityModel{}, err
}
return entity, nil
}
Create
func (r *EntityRepository) Create(ctx context.Context, entity model.EntityModel) (model.EntityModel, error) {
ctx, span := trace.Span(ctx, "EntityRepository.Create")
defer span.End()
err := gorm.G[model.EntityModel](r.DB).Create(ctx, &entity)
return entity, err
}
Update
func (r *EntityRepository) Update(ctx context.Context, entity model.EntityModel) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.Update")
defer otelSpan.End()
_, err := gorm.G[model.EntityModel](r.DB).Updates(ctx, entity)
if err != nil {
return model.EntityModel{}, err
}
return entity, nil
}
Delete
func (r *EntityRepository) Delete(ctx context.Context, id uint64) error {
ctx, span := trace.Span(ctx, "EntityRepository.Delete")
defer span.End()
rowsAffected, err := gorm.G[model.EntityModel](r.DB).
Where("id = ?", id).
Delete(ctx)
if err != nil {
return err
}
if rowsAffected == 0 {
return errs.ErrRecordNotFound
}
return nil
}
Custom Query (by field)
func (r *EntityRepository) FindByName(ctx context.Context, name string) (model.EntityModel, error) {
ctx, span := trace.Span(ctx, "EntityRepository.FindByName")
defer span.End()
entity, err := gorm.G[model.EntityModel](r.DB).
Where("name = ?", name).
Limit(1).
First(ctx)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return model.EntityModel{}, errs.ErrRecordNotFound
}
return model.EntityModel{}, err
}
return entity, nil
}
Transaction (relationship operations)
func (r *EntityRepository) AssignRelated(ctx context.Context, entityID uint64, relatedIDs []uint64) error {
ctx, span := trace.Span(ctx, "EntityRepository.AssignRelated")
defer span.End()
tx := r.DB.Begin()
_, err := gorm.G[model.EntityRelationModel](tx).
Where("entity_id = ?", entityID).
Delete(ctx)
if err != nil {
tx.Rollback()
return err
}
var relations []model.EntityRelationModel
for _, relatedID := range relatedIDs {
relations = append(relations, model.EntityRelationModel{
EntityID: entityID,
RelatedID: relatedID,
})
}
err = gorm.G[model.EntityRelationModel](tx).CreateInBatches(ctx, &relations, len(relations))
if err != nil {
tx.Rollback()
return err
}
if commitErr := tx.Commit().Error; commitErr != nil {
return commitErr
}
return nil
}
Fx Wiring
Add to internal/modules/<module>/fx.go:
fx.Provide(
fx.Annotate(
repository.NewEntityRepository,
fx.As(new(ports.EntityRepository)),
),
),
Critical Rules
- No standalone functions: When a file contains a struct with methods, do not add standalone functions. Use private methods on the struct instead.
- Struct: Embed
*database.PingoDBonly - Constructor: MUST return pointer
*EntityRepository - Interface assertion: Add
var _ ports.EntityRepository = (*EntityRepository)(nil)below struct - Tracing: Every method MUST start with
ctx, otelSpan := trace.Span(ctx, "Repo.Method")anddefer otelSpan.End() - Queries: Use
gorm.G[Model](r.DB)pattern for all queries - First queries: Add
.Limit(1)before.First(ctx) - Not found: Return
errs.ErrRecordNotFoundwhenerrors.Is(err, gorm.ErrRecordNotFound) - Delete: Check
rowsAffected == 0and returnerrs.ErrRecordNotFound - Transactions: Use
tx := r.DB.Begin(), rollback on error, commit at end - No comments: Do not add redundant comments above methods
- Validation: Run
make lintandmake nilawayafter generation - Add detailed comment on interfaces: Provide comprehensive comments on the port interfaces to describe their purpose and usage
Workflow
- Create port interface in
ports/<entity>_repository.go - Create repository implementation in
repository/<entity>_repository.go - Add Fx wiring to module’s
fx.go - Run
make lintto verify - Run
make nilawayfor static analysis