go-create-usecase
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Use Case Creator
Go 用例生成器
Core Principle
核心原则
Use cases NEVER depend on concrete implementations. Always depend on interfaces (ports) instead.
go
// ❌ WRONG - concrete implementation
type MyUseCase struct {
repo *repository.PostgresRepository
}
// ✅ CORRECT - interface (port)
type MyUseCase struct {
repo ports.MyRepository
}**用例绝不依赖具体实现。**始终依赖接口(ports)而非具体实现。
go
// ❌ WRONG - concrete implementation
type MyUseCase struct {
repo *repository.PostgresRepository
}
// ✅ CORRECT - interface (port)
type MyUseCase struct {
repo ports.MyRepository
}Structure Template
结构模板
go
package usecase
// 1. Input (skip if no params needed)
type <Name>Input struct {
Field string `validate:"required,max=255"`
}
// 2. Output
type <Name>Output struct {
Result string
}
// 3. UseCase struct
type <Name>UseCase struct {
// Dependencies (repos, ports, services)
// Always required:
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
// Optional:
validator validator.Validator // if input validation
}
// 4. Constructor
func New<Name>UseCase(
logger logger.Logger,
useCaseMetrics metrics.UseCaseMetrics,
) *<Name>UseCase {
return &<Name>UseCase{
logger: logger,
useCaseMetrics: useCaseMetrics,
}
}
// 5. Public Execute (metrics wrapper)
func (uc *<Name>UseCase) Execute(ctx context.Context, input <Name>Input) (<Name>Output, error) {
start := time.Now()
output, err := uc.execute(ctx, input)
uc.useCaseMetrics.ObserveDuration("metric_name", time.Since(start))
if err != nil {
uc.useCaseMetrics.IncError("metric_name")
return output, err
}
uc.useCaseMetrics.IncSuccess("metric_name")
return output, nil
}
// 6. Private execute (business logic)
func (uc *<Name>UseCase) execute(ctx context.Context, input <Name>Input) (<Name>Output, error) {
ctx, span := trace.Span(ctx, "<Name>UseCase.Execute")
defer span.End()
output := <Name>Output{}
// Validate if needed
if err := uc.validator.Validate(input); err != nil {
return output, err
}
// Business logic here
return output, nil
}go
package usecase
// 1. Input (skip if no params needed)
type <Name>Input struct {
Field string `validate:"required,max=255"`
}
// 2. Output
type <Name>Output struct {
Result string
}
// 3. UseCase struct
type <Name>UseCase struct {
// Dependencies (repos, ports, services)
// Always required:
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
// Optional:
validator validator.Validator // if input validation
}
// 4. Constructor
func New<Name>UseCase(
logger logger.Logger,
useCaseMetrics metrics.UseCaseMetrics,
) *<Name>UseCase {
return &<Name>UseCase{
logger: logger,
useCaseMetrics: useCaseMetrics,
}
}
// 5. Public Execute (metrics wrapper)
func (uc *<Name>UseCase) Execute(ctx context.Context, input <Name>Input) (<Name>Output, error) {
start := time.Now()
output, err := uc.execute(ctx, input)
uc.useCaseMetrics.ObserveDuration("metric_name", time.Since(start))
if err != nil {
uc.useCaseMetrics.IncError("metric_name")
return output, err
}
uc.useCaseMetrics.IncSuccess("metric_name")
return output, nil
}
// 6. Private execute (business logic)
func (uc *<Name>UseCase) execute(ctx context.Context, input <Name>Input) (<Name>Output, error) {
ctx, span := trace.Span(ctx, "<Name>UseCase.Execute")
defer span.End()
output := <Name>Output{}
// Validate if needed
if err := uc.validator.Validate(input); err != nil {
return output, err
}
// Business logic here
return output, nil
}Real Examples
实际示例
Example 1: Entity CRUD (ContactCreate)
示例1:实体CRUD(ContactCreate)
go
package usecase
type ContactCreateInput struct {
Name string `validate:"required,min=3,max=255"`
ContactType string `validate:"required,oneof=email webhook"`
ContactData string `validate:"required,max=500"`
}
type ContactCreateOutput struct {
ContactID uint64
Name string
ContactType string
}
type ContactCreateUseCase struct {
contactValidator ports.ContactValidator
contactRepository ports.ContactRepository
validator validator.Validator
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}File:
Metric:
contact_create_usecase.goMetric:
contact_creatego
package usecase
type ContactCreateInput struct {
Name string `validate:"required,min=3,max=255"`
ContactType string `validate:"required,oneof=email webhook"`
ContactData string `validate:"required,max=500"`
}
type ContactCreateOutput struct {
ContactID uint64
Name string
ContactType string
}
type ContactCreateUseCase struct {
contactValidator ports.ContactValidator
contactRepository ports.ContactRepository
validator validator.Validator
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}文件:
指标:
contact_create_usecase.go指标:
contact_createExample 2: File Upload (Non-entity)
示例2:文件上传(非实体)
go
package usecase
type FileUploadInput struct {
FileName string `validate:"required,max=255"`
ContentType string `validate:"required"`
Data []byte `validate:"required"`
}
type FileUploadOutput struct {
URL string
FileSize int64
UploadedAt time.Time
}
type FileUploadUseCase struct {
s3Client ports.S3Client
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}File:
Metric:
file_upload_usecase.goMetric:
file_uploadgo
package usecase
type FileUploadInput struct {
FileName string `validate:"required,max=255"`
ContentType string `validate:"required"`
Data []byte `validate:"required"`
}
type FileUploadOutput struct {
URL string
FileSize int64
UploadedAt time.Time
}
type FileUploadUseCase struct {
s3Client ports.S3Client
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}文件:
指标:
file_upload_usecase.go指标:
file_uploadExample 3: No Input (ContactList)
示例3:无输入参数(ContactList)
go
package usecase
type ContactListOutput struct {
Contacts []ContactListItem
}
type ContactListItem struct {
ContactID uint64
Name string
}
type ContactListUseCase struct {
contactRepository ports.ContactRepository
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}
func (uc *ContactListUseCase) Execute(ctx context.Context) (ContactListOutput, error) {
// No input parameter
}File:
Metric:
contact_list_usecase.goMetric:
contact_listgo
package usecase
type ContactListOutput struct {
Contacts []ContactListItem
}
type ContactListItem struct {
ContactID uint64
Name string
}
type ContactListUseCase struct {
contactRepository ports.ContactRepository
logger logger.Logger
useCaseMetrics metrics.UseCaseMetrics
}
func (uc *ContactListUseCase) Execute(ctx context.Context) (ContactListOutput, error) {
// No input parameter
}文件:
指标:
contact_list_usecase.go指标:
contact_listCommon Patterns
常见模式
Check existing record
检查现有记录
go
record, err := uc.repo.FindByX(ctx, value)
if err != nil && !errors.Is(err, shared_errs.ErrRecordNotFound) {
uc.logger.Error("error finding", logger.Error(err))
return output, err
}
if record.ID != 0 {
return output, errs.ErrAlreadyExists
}go
record, err := uc.repo.FindByX(ctx, value)
if err != nil && !errors.Is(err, shared_errs.ErrRecordNotFound) {
uc.logger.Error("error finding", logger.Error(err))
return output, err
}
if record.ID != 0 {
return output, errs.ErrAlreadyExists
}Enum conversion
枚举转换
go
enumVal, err := enum.NewTypeEnum(input.Type)
if err != nil {
return output, err
}
model.Type = enumVal.String()go
enumVal, err := enum.NewTypeEnum(input.Type)
if err != nil {
return output, err
}
model.Type = enumVal.String()List mapping
列表映射
go
items, err := uc.repo.FindAll(ctx)
if err != nil {
uc.logger.Error("error listing", logger.Error(err))
return output, err
}
output.Items = make([]Item, len(items))
for i, item := range items {
output.Items[i] = Item{ID: item.ID, Name: item.Name}
}go
items, err := uc.repo.FindAll(ctx)
if err != nil {
uc.logger.Error("error listing", logger.Error(err))
return output, err
}
output.Items = make([]Item, len(items))
for i, item := range items {
output.Items[i] = Item{ID: item.ID, Name: item.Name}
}Naming
命名规范
- Package: (always)
usecase - File: (e.g.,
<operation>_usecase.go,contact_create_usecase.go)file_upload_usecase.go - Struct: (e.g.,
<Operation>UseCase,ContactCreateUseCase)FileUploadUseCase - Metric: lowercase_underscore (e.g.,
<operation>,contact_create)file_upload - Span:
<StructName>.Execute
- 包名:(固定)
usecase - 文件:(例如:
<operation>_usecase.go、contact_create_usecase.go)file_upload_usecase.go - 结构体:(例如:
<Operation>UseCase、ContactCreateUseCase)FileUploadUseCase - 指标:小写下划线格式(例如:
<operation>、contact_create)file_upload - Span:
<StructName>.Execute
Required Imports
必要导入
go
package usecase
import (
"context"
"time"
"github.com/cristiano-pacheco/bricks/pkg/logger"
"github.com/cristiano-pacheco/bricks/pkg/otel/trace"
"github.com/cristiano-pacheco/pingo/internal/shared/metrics"
// Add as needed:
"github.com/cristiano-pacheco/bricks/pkg/validator"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/ports"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/model"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
)go
package usecase
import (
"context"
"time"
"github.com/cristiano-pacheco/bricks/pkg/logger"
"github.com/cristiano-pacheco/bricks/pkg/otel/trace"
"github.com/cristiano-pacheco/pingo/internal/shared/metrics"
// Add as needed:
"github.com/cristiano-pacheco/bricks/pkg/validator"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/ports"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/model"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/errs"
)Checklist
检查清单
- Input struct with validation tags (skip if no params)
- Output struct with result fields
- UseCase struct with logger + metrics + dependencies
- Constructor with all params
- Public Execute with metrics (ObserveDuration, IncError, IncSuccess)
- Private execute with trace span + business logic
- Place in
internal/modules/<module>/usecase/ - Wire in
fx.go - Run ,
make testandmake lintmake nilaway
- 带验证标签的输入结构体(无参数时可省略)
- 包含结果字段的输出结构体
- 包含logger + 指标 + 依赖项的UseCase结构体
- 包含所有参数的构造函数
- 带指标统计的公共Execute方法(ObserveDuration、IncError、IncSuccess)
- 带trace span + 业务逻辑的私有execute方法
- 放置在目录下
internal/modules/<module>/usecase/ - 在中进行依赖注入
fx.go - 运行、
make test和make lintmake nilaway