go-create-repository
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Create Repository
Go 仓库代码生成
Generate repository port interfaces and implementations for Pingo GO modular architechture conventions.
按照Pingo Go模块化架构规范生成仓库的端口接口与实现。
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
每个仓库需要两个文件:
- 端口接口:
internal/modules/<module>/ports/<entity>_repository.go - 仓库实现:
internal/modules/<module>/repository/<entity>_repository.go
Port Interface Structure
端口接口结构
Location:
internal/modules/<module>/ports/<entity>_repository.gogo
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:
go
FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error)Custom methods: Add domain-specific queries as needed (e.g., , ).
FindByNameAssignContacts位置:
internal/modules/<module>/ports/<entity>_repository.gogo
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
}分页变体:
go
FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error)自定义方法:根据需要添加领域特定的查询(如、)。
FindByNameAssignContactsRepository Implementation Structure
仓库实现结构
Location:
internal/modules/<module>/repository/<entity>_repository.gogo
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}
}位置:
internal/modules/<module>/repository/<entity>_repository.gogo
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)
FindAll(简单版)
go
func (r *EntityRepository) FindAll(ctx context.Context) ([]model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindAll")
defer otelSpan.End()
entities, err := gorm.G[model.EntityModel](r.DB).Find(ctx)
if err != nil {
return nil, err
}
return entities, nil
}go
func (r *EntityRepository) FindAll(ctx context.Context) ([]model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindAll")
defer otelSpan.End()
entities, err := gorm.G[model.EntityModel](r.DB).Find(ctx)
if err != nil {
return nil, err
}
return entities, nil
}FindAll (Paginated)
FindAll(分页版)
go
func (r *EntityRepository) FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindAll")
defer otelSpan.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
}go
func (r *EntityRepository) FindAll(ctx context.Context, page, pageSize int) ([]model.EntityModel, int64, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindAll")
defer otelSpan.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
FindByID
go
func (r *EntityRepository) FindByID(ctx context.Context, id uint64) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindByID")
defer otelSpan.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
}go
func (r *EntityRepository) FindByID(ctx context.Context, id uint64) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindByID")
defer otelSpan.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
Create
go
func (r *EntityRepository) Create(ctx context.Context, entity model.EntityModel) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.Create")
defer otelSpan.End()
err := gorm.G[model.EntityModel](r.DB).Create(ctx, &entity)
return entity, err
}go
func (r *EntityRepository) Create(ctx context.Context, entity model.EntityModel) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.Create")
defer otelSpan.End()
err := gorm.G[model.EntityModel](r.DB).Create(ctx, &entity)
return entity, err
}Update
Update
go
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
}go
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
Delete
go
func (r *EntityRepository) Delete(ctx context.Context, id uint64) error {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.Delete")
defer otelSpan.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
}go
func (r *EntityRepository) Delete(ctx context.Context, id uint64) error {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.Delete")
defer otelSpan.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)
自定义查询(按字段)
go
func (r *EntityRepository) FindByName(ctx context.Context, name string) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindByName")
defer otelSpan.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
}go
func (r *EntityRepository) FindByName(ctx context.Context, name string) (model.EntityModel, error) {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.FindByName")
defer otelSpan.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)
事务(关联操作)
go
func (r *EntityRepository) AssignRelated(ctx context.Context, entityID uint64, relatedIDs []uint64) error {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.AssignRelated")
defer otelSpan.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
}go
func (r *EntityRepository) AssignRelated(ctx context.Context, entityID uint64, relatedIDs []uint64) error {
ctx, otelSpan := trace.Span(ctx, "EntityRepository.AssignRelated")
defer otelSpan.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
Fx 依赖注入配置
Add to :
internal/modules/<module>/fx.gogo
fx.Provide(
fx.Annotate(
repository.NewEntityRepository,
fx.As(new(ports.EntityRepository)),
),
),添加到 :
internal/modules/<module>/fx.gogo
fx.Provide(
fx.Annotate(
repository.NewEntityRepository,
fx.As(new(ports.EntityRepository)),
),
),Critical Rules
关键规则
- Struct: Embed only
*database.PingoDB - Constructor: MUST return pointer
*EntityRepository - Interface assertion: Add below struct
var _ ports.EntityRepository = (*EntityRepository)(nil) - Tracing: Every method MUST start with and
ctx, otelSpan := trace.Span(ctx, "Repo.Method")defer otelSpan.End() - Queries: Use pattern for all queries
gorm.G[Model](r.DB) - First queries: Add before
.Limit(1).First(ctx) - Not found: Return when
errs.ErrRecordNotFounderrors.Is(err, gorm.ErrRecordNotFound) - Delete: Check and return
rowsAffected == 0errs.ErrRecordNotFound - Transactions: Use , rollback on error, commit at end
tx := r.DB.Begin() - No comments: Do not add redundant comments above methods
- Validation: Run and
make lintafter generationmake nilaway
- 结构体:仅嵌入
*database.PingoDB - 构造函数:必须返回指针类型
*EntityRepository - 接口断言:在结构体下方添加
var _ ports.EntityRepository = (*EntityRepository)(nil) - 链路追踪:每个方法必须以开头,并以
ctx, otelSpan := trace.Span(ctx, "Repo.Method")结尾defer otelSpan.End() - 查询语句:所有查询均使用模式
gorm.G[Model](r.DB) - First查询:在前添加
.First(ctx).Limit(1) - 未找到记录:当时,返回
errors.Is(err, gorm.ErrRecordNotFound)errs.ErrRecordNotFound - 删除操作:检查并返回
rowsAffected == 0errs.ErrRecordNotFound - 事务处理:使用开启事务,出错时回滚,最后提交
tx := r.DB.Begin() - 注释规范:不要在方法上方添加冗余注释
- 验证检查:生成代码后运行和
make lintmake nilaway
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 to verify
make lint - Run for static analysis
make nilaway
—