125 lines
2.9 KiB
Markdown
125 lines
2.9 KiB
Markdown
# Query Handler Scaffold Template
|
|
|
|
Generate a query handler file with a read model interface.
|
|
|
|
## Placeholders
|
|
|
|
- `{{Name}}` — PascalCase query name (e.g., `AvailableHours`)
|
|
- `{{name}}` — camelCase (e.g., `availableHours`)
|
|
- `{{name_snake}}` — snake_case (e.g., `available_hours`)
|
|
- `{{module}}` — Go module path from go.mod
|
|
- `{{Result}}` — Result type (e.g., `[]Date`, `*HourDetails`)
|
|
|
|
## File: `app/query/{{name_snake}}.go`
|
|
|
|
```go
|
|
package query
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"{{module_common}}/decorator"
|
|
)
|
|
|
|
// Read model — defines what data the query needs
|
|
// Implemented by adapters (repository or dedicated read store)
|
|
type {{Name}}ReadModel interface {
|
|
{{Name}}(ctx context.Context /* TODO: add query params */) ({{Result}}, error)
|
|
}
|
|
|
|
// 1. Query struct — noun phrase, plain data
|
|
type {{Name}} struct {
|
|
// TODO: Add query parameters
|
|
// Example:
|
|
// From time.Time
|
|
// To time.Time
|
|
}
|
|
|
|
// Result types — optimized for reading, may differ from domain entities
|
|
// type Date struct {
|
|
// Date time.Time
|
|
// Hours []Hour
|
|
// }
|
|
|
|
// 2. Exported handler type alias
|
|
type {{Name}}Handler decorator.QueryHandler[{{Name}}, {{Result}}]
|
|
|
|
// 3. Unexported concrete handler struct
|
|
type {{name}}Handler struct {
|
|
readModel {{Name}}ReadModel
|
|
}
|
|
|
|
// 4. Constructor with nil-checks + decorator wrapping
|
|
func New{{Name}}Handler(
|
|
readModel {{Name}}ReadModel,
|
|
logger *logrus.Entry,
|
|
metricsClient decorator.MetricsClient,
|
|
) {{Name}}Handler {
|
|
if readModel == nil {
|
|
panic("nil readModel")
|
|
}
|
|
if logger == nil {
|
|
panic("nil logger")
|
|
}
|
|
if metricsClient == nil {
|
|
panic("nil metricsClient")
|
|
}
|
|
|
|
return decorator.ApplyQueryDecorators[{{Name}}, {{Result}}](
|
|
{{name}}Handler{readModel: readModel},
|
|
logger,
|
|
metricsClient,
|
|
)
|
|
}
|
|
|
|
// Handle — delegates to read model, may add input validation
|
|
func (h {{name}}Handler) Handle(ctx context.Context, q {{Name}}) ({{Result}}, error) {
|
|
// TODO: Add input validation if needed
|
|
// Example:
|
|
// if q.From.After(q.To) {
|
|
// return nil, errors.NewIncorrectInputError("date-from-after-date-to", "date from is after date to")
|
|
// }
|
|
|
|
return h.readModel.{{Name}}(ctx /* TODO: pass query params */)
|
|
}
|
|
```
|
|
|
|
## Update `app/app.go`
|
|
|
|
Add to the `Queries` struct:
|
|
|
|
```go
|
|
type Queries struct {
|
|
// ... existing handlers ...
|
|
{{Name}} query.{{Name}}Handler
|
|
}
|
|
```
|
|
|
|
## Update `service/application.go`
|
|
|
|
Wire the handler. The read model is typically implemented by the same repository adapter or a dedicated read adapter:
|
|
|
|
```go
|
|
Queries: app.Queries{
|
|
// ... existing handlers ...
|
|
{{Name}}: query.New{{Name}}Handler(
|
|
{{entity}}Repository, // implements {{Name}}ReadModel
|
|
logger,
|
|
metricsClient,
|
|
),
|
|
},
|
|
```
|
|
|
|
## Implement ReadModel on Adapter
|
|
|
|
Add the read model method to your repository adapter:
|
|
|
|
```go
|
|
// In adapters/
|
|
func (r *Memory{{Entity}}Repository) {{Name}}(ctx context.Context /* params */) ({{Result}}, error) {
|
|
// TODO: Implement query against storage
|
|
}
|
|
```
|