npx skills add https://github.com/cristiano-pacheco/ai-rules --skill go-usecase为 Go 模块化架构生成用例实现。
每个操作一个文件:internal/modules/<module>/usecase/<名词>_<动作>_usecase.go
示例:user_create_usecase.go、product_update_usecase.go、order_cancel_usecase.go
给定名词 User 和动作 Create:
| 元素 | 名称 |
|---|---|
| 文件 |
Generate use case implementation for Go modular architecture.
One file per operation: internal/modules/<module>/usecase/<noun>_<action>_usecase.go
Examples: user_create_usecase.go, product_update_usecase.go, order_cancel_usecase.go
Given noun User and action Create:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
user_create_usecase.go| 结构体 | UserCreateUseCase |
| 输入 | UserCreateInput |
| 输出 | UserCreateOutput |
| 构造函数 | NewUserCreateUseCase |
| 方法 | Execute |
模式:结构体使用 名词动作 + UseCase。DTO 使用 名词动作 + Input / Output。
package usecase
import (
"context"
"github.com/cristiano-pacheco/bricks/pkg/logger"
"github.com/cristiano-pacheco/bricks/pkg/validator"
"github.com/cristiano-pacheco/gomies/internal/modules/<module>/model"
"github.com/cristiano-pacheco/gomies/internal/modules/<module>/ports"
)
type NounActionInput struct {
FirstName string `validate:"required,min=3,max=255"`
LastName string `validate:"required,min=3,max=255"`
Password string `validate:"required,min=8"`
Email string `validate:"required,email,max=255"`
}
type NounActionOutput struct {
FirstName string
LastName string
Email string
UserID uint64
}
type NounActionUseCase struct {
userRepository ports.UserRepository
validator validator.Validator
logger logger.Logger
}
func NewNounActionUseCase(
userRepository ports.UserRepository,
validator validator.Validator,
logger logger.Logger,
) *NounActionUseCase {
return &NounActionUseCase{
validator: validator,
logger: logger,
}
}
func (uc *NounActionUseCase) Execute(ctx context.Context, input NounActionInput) (NounActionOutput, error) {
err := uc.validator.Struct(input)
if err != nil {
uc.logger.Error("user creation validation failed", logger.Error(err))
return NounActionOutput{}, err
}
userModel := model.UserModel{
FirstName: input.FirstName,
LastName: input.LastName,
Email: input.Email,
Password: input.Password,
}
createdUser, err := uc.userRepository.Create(ctx, userModel)
if err != nil {
uc.logger.Error("user creation failed", logger.Error(err))
return NounActionOutput{}, err
}
output := NounActionOutput{
UserID: createdUser.ID,
FirstName: createdUser.FirstName,
LastName: createdUser.LastName,
Email: createdUser.Email,
}
return output, nil
}
uc.validator.Struct(input)errs 包通过 Input 结构体上的 validate 结构体标签定义约束。在 Execute 内部的第一行调用 uc.validator.Struct(input)。直接返回验证错误 — 共享的验证器会格式化它们。
常用标签:required、min=N、max=N、email、oneof=val1 val2 val3
来自仓库、服务或外部调用的每个错误在返回前必须被记录:
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
uc.logger.Error("error finding entity by id", logger.Error(err))
return EntityGetOutput{}, err
}
对于预期不存在(非致命错误)的未找到检查:
entity, err := uc.entityRepo.FindByEmail(ctx, input.Email)
if err != nil && !errors.Is(err, bricserrs.ErrRecordNotFound) {
uc.logger.Error("error finding entity by email", logger.Error(err))
return EntityCreateOutput{}, err
}
if entity.ID != 0 {
return EntityCreateOutput{}, errs.ErrEmailAlreadyInUse
}
导入 bricks 错误:bricserrs "github.com/cristiano-pacheco/bricks/errs"
返回来自 errs 的类型化模块错误 — 永远不要使用 errors.New(...)。
每个用例都包含这些共享依赖项:
validator.Validator — 通过标签进行输入结构体验证logger.Logger — 错误日志记录永远不要为模块依赖项注入具体类型。
在模块的 fx.go 中,注册原始构造函数和一个包装所有用例的 provideDecoratedUseCases 函数。
var Module = fx.Module(
"<module-name>",
fx.Provide(
usecase.NewEntityCreateUseCase,
provideDecoratedUseCases,
),
)
type decorateUseCasesIn struct {
fx.In
UseCaseDecoratorFactory *ucdecorator.Factory
EntityCreateUseCase *usecase.EntityCreateUseCase
}
type decorateUseCasesOut struct {
fx.Out
EntityCreateUseCase ucdecorator.UseCase[usecase.EntityCreateInput, usecase.EntityCreateOutput]
}
func provideDecoratedUseCases(in decorateUseCasesIn) decorateUseCasesOut {
return decorateUseCasesOut{
EntityCreateUseCase: ucdecorator.Wrap(in.UseCaseDecoratorFactory, in.EntityCreateUseCase),
}
}
func (uc *UserCreateUseCase) Execute(ctx context.Context, input UserCreateInput) (UserCreateOutput, error) {
// 错误:跳过了 uc.validator.Struct(input)
user, err := uc.userRepository.Create(ctx, ...)
// 错误
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
return EntityGetOutput{}, err
}
// 正确
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
uc.logger.Error("error finding entity by id", logger.Error(err))
return EntityGetOutput{}, err
}
// 错误
return UserCreateOutput{}, errors.New("email already in use")
// 正确
return UserCreateOutput{}, errs.ErrEmailAlreadyInUse
// 错误:可观测性应属于 ucdecorator
ctx, span := trace.Span(ctx, "UserCreateUseCase.Execute")
defer span.End()
// 错误
type UserCreateUseCase struct {
userRepository *repository.UserRepository
}
// 正确
type UserCreateUseCase struct {
userRepository ports.UserRepository
}
// 错误:Input/Output 未遵循名词动作模式
type CreateUserInput struct {}
type CreateUserOutput struct {}
// 正确
type UserCreateInput struct {}
type UserCreateOutput struct {}
// 错误
// Execute executes the user create use case.
func (uc *UserCreateUseCase) Execute(...) {}
// NewUserCreateUseCase creates a new UserCreateUseCase.
func NewUserCreateUseCase(...) *UserCreateUseCase {}
每个错误都必须被记录。每个用例必须在其自己的用例文件中定义自己的 Input 和 Output 类型,并且这些边界类型必须是自包含的。Input/Output 绝对不能嵌入或引用共享模块的 DTO/模型。Input/Output 必须显式声明所有字段,可以直接声明,也可以通过在同一用例文件中声明的嵌套结构体声明。Input/Output 类型是该用例契约私有的,绝不能由其他用例重用。共享的形状应属于仓库/服务/领域层,而不是用例边界契约。Input/Output 类型不能有 json 标签。
NounActionUseCase,输入 NounActionInput,输出 NounActionOutput。没有例外。uc.validator.Struct(input) 始终是 Execute 中的第一个调用。uc.logger.Error(msg, logger.Error(err))。errs 包的错误 — 永远不要使用 errors.New(...)。ucdecorator 处理。ports.* 接口。Execute(ctx context.Context, input Input) (Output, error)。NewNounActionUseCase(...) 返回 *NounActionUseCase。Execute 或私有方法中。fx.In/fx.Out 结构体使用 ucdecorator.Wrap 包装。当文件中存在带有方法的结构体时,禁止在包级别使用独立函数。它们会污染包命名空间,可能与其他服务文件中的助手函数冲突,并分散属于该结构体的逻辑。
usecase/<名词>_<动作>_usecase.goExecutefx.go(构造函数 + provideDecoratedUseCases)make lint 和 make nilaway 以验证用例遵循所有模式且没有空指针风险。每周安装数
1
仓库
首次出现
今天
安全审计
安装于
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1
| Element | Name |
|---|
| File | user_create_usecase.go |
| Struct | UserCreateUseCase |
| Input | UserCreateInput |
| Output | UserCreateOutput |
| Constructor | NewUserCreateUseCase |
| Method | Execute |
Pattern: NounAction + UseCase for the struct. NounAction + Input / Output for DTOs.
package usecase
import (
"context"
"github.com/cristiano-pacheco/bricks/pkg/logger"
"github.com/cristiano-pacheco/bricks/pkg/validator"
"github.com/cristiano-pacheco/gomies/internal/modules/<module>/model"
"github.com/cristiano-pacheco/gomies/internal/modules/<module>/ports"
)
type NounActionInput struct {
FirstName string `validate:"required,min=3,max=255"`
LastName string `validate:"required,min=3,max=255"`
Password string `validate:"required,min=8"`
Email string `validate:"required,email,max=255"`
}
type NounActionOutput struct {
FirstName string
LastName string
Email string
UserID uint64
}
type NounActionUseCase struct {
userRepository ports.UserRepository
validator validator.Validator
logger logger.Logger
}
func NewNounActionUseCase(
userRepository ports.UserRepository,
validator validator.Validator,
logger logger.Logger,
) *NounActionUseCase {
return &NounActionUseCase{
validator: validator,
logger: logger,
}
}
func (uc *NounActionUseCase) Execute(ctx context.Context, input NounActionInput) (NounActionOutput, error) {
err := uc.validator.Struct(input)
if err != nil {
uc.logger.Error("user creation validation failed", logger.Error(err))
return NounActionOutput{}, err
}
userModel := model.UserModel{
FirstName: input.FirstName,
LastName: input.LastName,
Email: input.Email,
Password: input.Password,
}
createdUser, err := uc.userRepository.Create(ctx, userModel)
if err != nil {
uc.logger.Error("user creation failed", logger.Error(err))
return NounActionOutput{}, err
}
output := NounActionOutput{
UserID: createdUser.ID,
FirstName: createdUser.FirstName,
LastName: createdUser.LastName,
Email: createdUser.Email,
}
return output, nil
}
uc.validator.Struct(input)errs packageDefine constraints via validate struct tags on the Input struct. Call uc.validator.Struct(input) as the first line inside Execute. Return validation errors directly — the shared validator formats them.
Common tags: required, min=N, max=N, email, oneof=val1 val2 val3
Every error from a repository, service, or external call MUST be logged before returning:
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
uc.logger.Error("error finding entity by id", logger.Error(err))
return EntityGetOutput{}, err
}
For not-found checks where absence is expected (not a terminal error):
entity, err := uc.entityRepo.FindByEmail(ctx, input.Email)
if err != nil && !errors.Is(err, bricserrs.ErrRecordNotFound) {
uc.logger.Error("error finding entity by email", logger.Error(err))
return EntityCreateOutput{}, err
}
if entity.ID != 0 {
return EntityCreateOutput{}, errs.ErrEmailAlreadyInUse
}
Import for bricks errors: bricserrs "github.com/cristiano-pacheco/bricks/errs"
Return typed module errors from errs — never errors.New(...).
Every use case includes these shared dependencies:
validator.Validator — input struct validation via tagslogger.Logger — error loggingNever inject concrete types for module dependencies.
In the module's fx.go, register raw constructors and a single provideDecoratedUseCases function that wraps them all.
var Module = fx.Module(
"<module-name>",
fx.Provide(
usecase.NewEntityCreateUseCase,
provideDecoratedUseCases,
),
)
type decorateUseCasesIn struct {
fx.In
UseCaseDecoratorFactory *ucdecorator.Factory
EntityCreateUseCase *usecase.EntityCreateUseCase
}
type decorateUseCasesOut struct {
fx.Out
EntityCreateUseCase ucdecorator.UseCase[usecase.EntityCreateInput, usecase.EntityCreateOutput]
}
func provideDecoratedUseCases(in decorateUseCasesIn) decorateUseCasesOut {
return decorateUseCasesOut{
EntityCreateUseCase: ucdecorator.Wrap(in.UseCaseDecoratorFactory, in.EntityCreateUseCase),
}
}
func (uc *UserCreateUseCase) Execute(ctx context.Context, input UserCreateInput) (UserCreateOutput, error) {
// BAD: skipped uc.validator.Struct(input)
user, err := uc.userRepository.Create(ctx, ...)
// BAD
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
return EntityGetOutput{}, err
}
// GOOD
entity, err := uc.entityRepo.FindByID(ctx, input.ID)
if err != nil {
uc.logger.Error("error finding entity by id", logger.Error(err))
return EntityGetOutput{}, err
}
// BAD
return UserCreateOutput{}, errors.New("email already in use")
// GOOD
return UserCreateOutput{}, errs.ErrEmailAlreadyInUse
// BAD: observability belongs in ucdecorator
ctx, span := trace.Span(ctx, "UserCreateUseCase.Execute")
defer span.End()
// BAD
type UserCreateUseCase struct {
userRepository *repository.UserRepository
}
// GOOD
type UserCreateUseCase struct {
userRepository ports.UserRepository
}
// BAD: Input/Output not following NounAction pattern
type CreateUserInput struct {}
type CreateUserOutput struct {}
// GOOD
type UserCreateInput struct {}
type UserCreateOutput struct {}
// BAD
// Execute executes the user create use case.
func (uc *UserCreateUseCase) Execute(...) {}
// NewUserCreateUseCase creates a new UserCreateUseCase.
func NewUserCreateUseCase(...) *UserCreateUseCase {}
Every error must be logged. Each use case must define its own Input and Output types in its own use case file, and those boundary types must be self-contained. Input/Output MUST NOT embed or reference shared module DTOs/models Input/Output MUST declare all fields explicitly, either directly or via nested structs declared in the same use case file. Input/Output types are private to that use case contract and MUST NOT be reused by other use cases. Shared shapes belong in repository/service/domain layers, not in use case boundary contracts. Input/Output types must not have json tags
NounActionUseCase, Input NounActionInput, Output NounActionOutput. No exceptions.uc.validator.Struct(input) is always the first call in Execute.uc.logger.Error(msg, logger.Error(err)) before every error return.errs package — never errors.New(...).ucdecorator externally.ports.* interfaces.Execute(ctx context.Context, input Input) (Output, error).NewNounActionUseCase(...) returns *NounActionUseCase.Execute or private methods only.ucdecorator.Wrap via fx.In/fx.Out structs.Standalone functions at the package level are forbidden when a struct with methods exists in the file. They pollute the package namespace, can collide with helpers in other service files, and fragment logic that belongs to the struct.
usecase/<noun>_<action>_usecase.goExecutefx.go (constructor + provideDecoratedUseCases)make lint and make nilaway to verify the use case follows all patterns and has no nil pointer risks.Weekly Installs
1
Repository
First Seen
Today
Security Audits
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
147,400 周安装