3.4 KiB
3.4 KiB
Event Publisher Adapter Scaffold Template
Generate a Watermill publisher adapter that implements a domain/app-layer interface. The adapter lives in adapters/ and translates domain operations into published messages. The interface lives in app/command/services.go.
Placeholders
{{Name}}— PascalCase aggregate name (e.g.,Training){{name}}— camelCase (e.g.,training){{name_snake}}— snake_case (e.g.,training){{name_lower}}— all lowercase (e.g.,training){{module}}— Go module path from go.mod{{event}}— PascalCase first event name (e.g.,TrainingScheduled){{topic}}— Dot-notation topic (e.g.,training.scheduled)
File 1: app/command/services.go
If this file already exists, add the interface. Otherwise create it:
package command
import "context"
// {{Name}}EventPublisher defines events that can be emitted for {{name_lower}} operations.
// Implemented by adapters (e.g., Watermill AMQP adapter).
type {{Name}}EventPublisher interface {
{{event}}(ctx context.Context) error
// TODO: Add more event methods as needed
// Example:
// {{Name}}Cancelled(ctx context.Context, uuid string) error
}
File 2: adapters/{{name_snake}}_event_publisher.go
package adapters
import (
"context"
"encoding/json"
"github.com/ThreeDotsLabs/watermill"
"github.com/ThreeDotsLabs/watermill/message"
"github.com/ThreeDotsLabs/watermill/message/router/middleware"
)
type Watermill{{Name}}EventPublisher struct {
pub message.Publisher
}
func NewWatermill{{Name}}EventPublisher(pub message.Publisher) Watermill{{Name}}EventPublisher {
return Watermill{{Name}}EventPublisher{pub: pub}
}
// {{event}}Event is the wire format for the {{topic}} topic.
type {{event}}Event struct {
// TODO: Add event payload fields
// Example:
// UUID string `json:"uuid"`
// Hour time.Time `json:"hour"`
}
func (p Watermill{{Name}}EventPublisher) {{event}}(ctx context.Context) error {
event := {{event}}Event{
// TODO: Map domain data to event fields
}
payload, err := json.Marshal(event)
if err != nil {
return err
}
msg := message.NewMessage(watermill.NewUUID(), payload)
middleware.SetCorrelationID(watermill.NewUUID(), msg)
return p.pub.Publish("{{topic}}", msg)
}
Update service/application.go
Wire the publisher adapter in the composition root:
func NewApplication(ctx context.Context) (app.Application, func()) {
// ... existing clients ...
publisher, closePub, err := client.NewWatermillPublisher()
if err != nil { panic(err) }
eventPublisher := adapters.NewWatermill{{Name}}EventPublisher(publisher)
return newApplication(ctx, eventPublisher),
func() {
// ... existing cleanup ...
_ = closePub()
}
}
Update the private newApplication to accept the publisher interface:
func newApplication(
ctx context.Context,
eventPublisher command.{{Name}}EventPublisher,
// ... existing deps ...
) app.Application {
// ... pass eventPublisher to command handlers that need it
}
Update command handler
Inject the publisher into the command handler that triggers the event:
type {{name}}Handler struct {
{{name_lower}}Repo {{name_lower}}.Repository
eventPublisher command.{{Name}}EventPublisher
}
func (h {{name}}Handler) Handle(ctx context.Context, cmd {{command}}) error {
// ... domain logic ...
return h.eventPublisher.{{event}}(ctx)
}