Creating Commands
Commands represent actions or requests that change the state of your application. This guide will walk you through the process of creating and handling commands using Watermill.
Defining a Command
- Create a new Go file in the
model
package to define your command structure. - Define a struct that represents your command, including all relevant fields.
Example:
go
package model
type CreateInstallMetafieldCmd struct {
ShopID string `json:"shop_id"`
MetafieldKey string `json:"metafield_key"`
MetafieldValue string `json:"metafield_value"`
}
package model
type CreateInstallMetafieldCmd struct {
ShopID string `json:"shop_id"`
MetafieldKey string `json:"metafield_key"`
MetafieldValue string `json:"metafield_value"`
}
Creating a Command Handler
- Create a new file in the
feature/<your-feature>/command
directory. - Define a struct that implements the
cqrs.CommandHandler
interface. This interface requires three methods:HandlerName() string
: Returns the name of the handler.NewCommand() interface{}
: Returns a new instance of the command struct.Handle(ctx context.Context, cmd interface{}) error
: Contains the logic to handle the command.
Example:
go
package command
import (
"context"
"github.com/ThreeDotsLabs/watermill/components/cqrs"
"github.com/aiocean/wireset/model"
"go.uber.org/zap"
)
type CreateInstallMetafieldHandler struct {
Logger *zap.Logger
EventBus *cqrs.EventBus
CommandBus *cqrs.CommandBus
}
func (h *CreateInstallMetafieldHandler) HandlerName() string {
return "CreateInstallMetafieldHandler"
}
func (h *CreateInstallMetafieldHandler) NewCommand() interface{} {
return &model.CreateInstallMetafieldCmd{}
}
func (h *CreateInstallMetafieldHandler) Handle(ctx context.Context, cmd interface{}) error {
command, ok := cmd.(*model.CreateInstallMetafieldCmd)
if !ok {
return ErrInvalidCommandType
}
// Handle the command here
h.Logger.Info("CreateInstallMetafield command handled", zap.String("shop_id", command.ShopID))
return nil
}
package command
import (
"context"
"github.com/ThreeDotsLabs/watermill/components/cqrs"
"github.com/aiocean/wireset/model"
"go.uber.org/zap"
)
type CreateInstallMetafieldHandler struct {
Logger *zap.Logger
EventBus *cqrs.EventBus
CommandBus *cqrs.CommandBus
}
func (h *CreateInstallMetafieldHandler) HandlerName() string {
return "CreateInstallMetafieldHandler"
}
func (h *CreateInstallMetafieldHandler) NewCommand() interface{} {
return &model.CreateInstallMetafieldCmd{}
}
func (h *CreateInstallMetafieldHandler) Handle(ctx context.Context, cmd interface{}) error {
command, ok := cmd.(*model.CreateInstallMetafieldCmd)
if !ok {
return ErrInvalidCommandType
}
// Handle the command here
h.Logger.Info("CreateInstallMetafield command handled", zap.String("shop_id", command.ShopID))
return nil
}
Registering the Command Handler
In your feature struct, you add the command handler as a field, ensure you have the CommandProcessor in the wireset:
go
type Feature struct {
// other fields
CommandProcessor *cqrs.CommandProcessor
CreateInstallMetafieldHandler *command.CreateInstallMetafieldHandler
}
type Feature struct {
// other fields
CommandProcessor *cqrs.CommandProcessor
CreateInstallMetafieldHandler *command.CreateInstallMetafieldHandler
}
then declare it as an injectable dependency:
go
var DefaultWireset = wire.NewSet(
wire.Struct(new(Feature), "*"),
wire.Struct(new(command.CreateInstallMetafieldHandler), "*"),
)
var DefaultWireset = wire.NewSet(
wire.Struct(new(Feature), "*"),
wire.Struct(new(command.CreateInstallMetafieldHandler), "*"),
)
in the feature's init function, you need to register the command handler to the command processor:
go
func (f *Feature) Init() error {
if err := f.CommandProcessor.AddHandlers(f.CreateInstallMetafieldHandler); err != nil {
return err
}
return nil
}
func (f *Feature) Init() error {
if err := f.CommandProcessor.AddHandlers(f.CreateInstallMetafieldHandler); err != nil {
return err
}
return nil
}
finally, need to run the wire command to generate the code:
bash
wire gen ./cmd/server/...
wire gen ./cmd/server/...
Dispatching Commands
To dispatch a command:
- Create a new instance of your command struct.
- Use
commandBus.Send(ctx, command)
to dispatch the command.
Example:
go
cmd := &model.CreateInstallMetafieldCmd{
ShopID: "shop123",
MetafieldKey: "key",
MetafieldValue: "value",
}
err := commandBus.Send(ctx, cmd)
if err != nil {
// Handle error
}
cmd := &model.CreateInstallMetafieldCmd{
ShopID: "shop123",
MetafieldKey: "key",
MetafieldValue: "value",
}
err := commandBus.Send(ctx, cmd)
if err != nil {
// Handle error
}
This guide provides a basic overview of creating and handling commands. For more advanced use cases, refer to the Watermill documentation.