Go Create UseCase
Generate a use case that depends on ports (interfaces), not concrete implementations.
Create the file
Create one file per operation in: internal/modules/<module>/usecase/<operation>_usecase.go
Use:
-
package: usecase
-
struct name: <Operation>UseCase
-
method name: Execute
Naming (CRITICAL)
Apply consistent naming for every use case.
Rules:
-
file: <operation>_usecase.go
-
input DTO: <Operation>Input
-
output DTO: <Operation>Output
-
use case struct: <Operation>UseCase
-
constructor: New<Operation>UseCase
Example (contact_create ):
-
file: contact_create_usecase.go
-
input: ContactCreateInput
-
output: ContactCreateOutput
-
struct: ContactCreateUseCase
-
constructor: NewContactCreateUseCase
Example (contact_list , no real input):
-
file: contact_list_usecase.go
-
input: ContactListInput (empty struct)
-
output: ContactListOutput
-
struct: ContactListUseCase
-
constructor: NewContactListUseCase
Follow the structure (CRITICAL)
Implement this order in the file:
-
Input struct (ALWAYS present; can be empty)
-
Output struct (ALWAYS present; can be empty)
-
Use case struct with dependencies
-
Constructor New<Operation>UseCase
-
Public Execute method (contains all business logic)
-
Input and Output must NOT CONTAIN json tags, only validation tags when needed for input.
Current architecture rule
Use cases contain business logic only.
Do NOT include in usecases:
-
logger dependencies
-
metrics dependencies
-
tracing code
-
private execute method wrappers
Observability and error translation are handled by ucdecorator in Fx wiring.
Use this template
package usecase
import ( "context"
"github.com/cristiano-pacheco/bricks/pkg/validator"
"github.com/cristiano-pacheco/catzi/internal/modules/<module>/ports"
)
type <Operation>Input struct {
Field string validate:"required,max=255"
}
type <Operation>Output struct { Result string }
type <Operation>UseCase struct { repo ports.<Entity>Repository validator validator.Validator // include only if needed }
func New<Operation>UseCase( repo ports.<Entity>Repository, validator validator.Validator, ) *<Operation>UseCase { return &<Operation>UseCase{ repo: repo, validator: validator, } }
func (uc *<Operation>UseCase) Execute(ctx context.Context, input <Operation>Input) (<Operation>Output, error) { if err := uc.validator.Validate(input); err != nil { return <Operation>Output{}, err }
// Add business orchestration here
// - read/write via repositories
// - call domain services
// - map model to output DTO
return <Operation>Output{}, nil
}
Apply variants
No-input use case
When no parameters are needed, still define an empty input:
type ContactListInput struct{}
And keep the same contract:
func (uc *ContactListUseCase) Execute(ctx context.Context, input ContactListInput) (ContactListOutput, error)
No-output use case
When no result payload is needed, define an empty output:
type ContactDeleteOutput struct{}
And return it:
return ContactDeleteOutput{}, nil
No-validation use case
When validation is not required, remove validator.Validator from dependencies and skip validation.
Multi-dependency orchestration
Inject multiple ports as interfaces (repositories, caches, services) in the use case struct and constructor.
Apply common patterns
Check existing record before create
import brickserrors "github.com/cristiano-pacheco/pkg/errs"
record, err := uc.repo.FindByX(ctx, input.Field) if err != nil && !errors.Is(err, brickserrors.ErrRecordNotFound) { return output, err } if record.ID != 0 { return output, brickserrors.ErrAlreadyExists }
Convert enum from input
enumVal, err := enum.NewTypeEnum(input.Type) if err != nil { return output, err } model.Type = enumVal.String()
Map list response
items, err := uc.repo.FindAll(ctx) if err != nil { return output, err }
output.Items = make([]ItemOutput, len(items)) for i, item := range items { output.Items[i] = ItemOutput{ID: item.ID, Name: item.Name} }
Wire with Fx
Register raw usecases and decorate them via ucdecorator .
Minimal provider example
fx.Provide( usecase.New<Operation>UseCase, )
Decorator wiring pattern (recommended)
Use a consolidated provider (fx.In
- fx.Out ) and wrap usecases with:
ucdecorator.Wrap(factory, rawUseCase)
Wrap infers:
-
usecase name (e.g. CategoryCreateUseCase.Execute )
-
metric name (e.g. category_create )
No need to pass metric/usecase name strings manually.
Full module wiring pattern (single-file, fx.In
- fx.Out )
Use this when the module has multiple usecases and you want less boilerplate in fx.go .
type decorateIn struct { fx.In
Factory *ucdecorator.Factory
Create *usecase.<Entity>CreateUseCase
List *usecase.<Entity>ListUseCase
}
type decorateOut struct { fx.Out
Create ucdecorator.UseCase[usecase.<Entity>CreateInput, usecase.<Entity>CreateOutput]
List ucdecorator.UseCase[usecase.<Entity>ListInput, usecase.<Entity>ListOutput]
}
func provideDecoratedUseCases(in decorateIn) decorateOut { return decorateOut{ Create: ucdecorator.Wrap(in.Factory, in.Create), List: ucdecorator.Wrap(in.Factory, in.List), } }
var Module = fx.Module( "<module>", fx.Provide( // repositories/services/validators // raw usecases usecase.New<Entity>CreateUseCase, usecase.New<Entity>ListUseCase,
// decorated usecases
provideDecoratedUseCases,
// handlers/routers
),
)
This keeps:
-
Raw constructors simple
-
Decoration centralized in one provider
-
Handler injection strongly typed via ucdecorator.UseCase[Input, Output]
Enforce rules
-
Depend only on ports.* interfaces in use cases.
-
Keep orchestration in use case; keep persistence in repositories.
-
Use a single public Execute method; do not create a private execute wrapper.
-
Always define both Input and Output structs (use empty struct when needed).
-
Keep naming consistent across file, structs, constructor, and method.
-
Return typed output DTOs; do not leak persistence models directly.
-
Keep observability and translation outside usecases (via decorators).
Final checklist
-
Create internal/modules/<module>/usecase/<operation>_usecase.go .
-
Add Input/Output DTOs for the operation (including empty structs when needed).
-
Inject required ports/services in constructor.
-
Implement a single Execute with all business logic.
-
Wire raw usecase in Fx and decorate with ucdecorator.Wrap(factory, raw) .
-
Create unit tests using the go-unit-tests skill.
-
Run make test .
-
Run make lint .
-
Run make nilaway .