snapshot of https://github.com/ThreeDotsLabs/watermill.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
master / ci (push) Failing after 0s
Some checks failed
master / ci (push) Failing after 0s
This commit is contained in:
220
_examples/real-world-examples/delayed-messages/main.go
Normal file
220
_examples/real-world-examples/delayed-messages/main.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
stdSQL "database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill-redisstream/pkg/redisstream"
|
||||
"github.com/ThreeDotsLabs/watermill-sql/v4/pkg/sql"
|
||||
"github.com/ThreeDotsLabs/watermill/components/cqrs"
|
||||
"github.com/ThreeDotsLabs/watermill/components/delay"
|
||||
"github.com/ThreeDotsLabs/watermill/components/forwarder"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := stdSQL.Open("postgres", "postgres://watermill:password@postgres:5432/watermill?sslmode=disable")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logger := watermill.NewStdLogger(false, false)
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: "redis:6379"})
|
||||
marshaler := cqrs.JSONMarshaler{
|
||||
GenerateName: cqrs.StructName,
|
||||
}
|
||||
|
||||
redisPublisher, err := redisstream.NewPublisher(redisstream.PublisherConfig{
|
||||
Client: redisClient,
|
||||
}, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var sqlPublisher message.Publisher
|
||||
sqlPublisher, err = sql.NewDelayedPostgreSQLPublisher(sql.BeginnerFromStdSQL(db), sql.DelayedPostgreSQLPublisherConfig{
|
||||
DelayPublisherConfig: delay.PublisherConfig{},
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sqlPublisher = forwarder.NewPublisher(sqlPublisher, forwarder.PublisherConfig{
|
||||
ForwarderTopic: "forwarder",
|
||||
})
|
||||
|
||||
eventBus, err := cqrs.NewEventBusWithConfig(redisPublisher, cqrs.EventBusConfig{
|
||||
GeneratePublishTopic: func(params cqrs.GenerateEventPublishTopicParams) (string, error) {
|
||||
return params.EventName, nil
|
||||
},
|
||||
Marshaler: marshaler,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
commandBus, err := cqrs.NewCommandBusWithConfig(sqlPublisher, cqrs.CommandBusConfig{
|
||||
GeneratePublishTopic: func(params cqrs.CommandBusGeneratePublishTopicParams) (string, error) {
|
||||
return params.CommandName, nil
|
||||
},
|
||||
Marshaler: marshaler,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router := message.NewDefaultRouter(logger)
|
||||
|
||||
eventProcessor, err := cqrs.NewEventProcessorWithConfig(router, cqrs.EventProcessorConfig{
|
||||
GenerateSubscribeTopic: func(params cqrs.EventProcessorGenerateSubscribeTopicParams) (string, error) {
|
||||
return params.EventName, nil
|
||||
},
|
||||
SubscriberConstructor: func(params cqrs.EventProcessorSubscriberConstructorParams) (message.Subscriber, error) {
|
||||
return redisstream.NewSubscriber(redisstream.SubscriberConfig{
|
||||
Client: redisClient,
|
||||
ConsumerGroup: params.HandlerName,
|
||||
}, logger)
|
||||
},
|
||||
Marshaler: marshaler,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
commandProcessor, err := cqrs.NewCommandProcessorWithConfig(router, cqrs.CommandProcessorConfig{
|
||||
GenerateSubscribeTopic: func(params cqrs.CommandProcessorGenerateSubscribeTopicParams) (string, error) {
|
||||
return params.CommandName, nil
|
||||
},
|
||||
SubscriberConstructor: func(params cqrs.CommandProcessorSubscriberConstructorParams) (message.Subscriber, error) {
|
||||
return redisstream.NewSubscriber(redisstream.SubscriberConfig{
|
||||
Client: redisClient,
|
||||
ConsumerGroup: params.HandlerName,
|
||||
}, logger)
|
||||
},
|
||||
Marshaler: marshaler,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = eventProcessor.AddHandlers(
|
||||
cqrs.NewEventHandler(
|
||||
"OnOrderPlacedHandler",
|
||||
func(ctx context.Context, event *OrderPlaced) error {
|
||||
fmt.Printf("💰 Received order from %v <%v>\n", event.Customer.Name, event.Customer.Email)
|
||||
|
||||
cmd := SendFeedbackForm{
|
||||
To: event.Customer.Email,
|
||||
Name: event.Customer.Name,
|
||||
}
|
||||
|
||||
// In a real world scenario, we would delay the command by a few days
|
||||
ctx = delay.WithContext(ctx, delay.For(8*time.Second))
|
||||
|
||||
err := commandBus.Send(ctx, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = commandProcessor.AddHandlers(
|
||||
cqrs.NewCommandHandler(
|
||||
"OnSendFeedbackForm",
|
||||
func(ctx context.Context, cmd *SendFeedbackForm) error {
|
||||
fmt.Printf("📧 Sending feedback form to %v <%v>\n", cmd.Name, cmd.To)
|
||||
|
||||
// In a real world scenario, we would send an email to the customer here
|
||||
|
||||
return nil
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sqlSubscriber, err := sql.NewDelayedPostgreSQLSubscriber(sql.BeginnerFromStdSQL(db), sql.DelayedPostgreSQLSubscriberConfig{
|
||||
DeleteOnAck: true,
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = forwarder.NewForwarder(
|
||||
sqlSubscriber,
|
||||
redisPublisher,
|
||||
logger,
|
||||
forwarder.Config{
|
||||
ForwarderTopic: "forwarder",
|
||||
Router: router,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
err = router.Run(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-router.Running()
|
||||
|
||||
for {
|
||||
name := gofakeit.FirstName()
|
||||
e := OrderPlaced{
|
||||
OrderID: uuid.NewString(),
|
||||
Customer: Customer{
|
||||
Name: name,
|
||||
Email: fmt.Sprintf("%v@%v", strings.ToLower(name), gofakeit.DomainName()),
|
||||
},
|
||||
}
|
||||
|
||||
err = eventBus.Publish(context.Background(), e)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
type Customer struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type OrderPlaced struct {
|
||||
OrderID string `json:"order_id"`
|
||||
Customer Customer `json:"customer"`
|
||||
}
|
||||
|
||||
type SendFeedbackForm struct {
|
||||
To string `json:"to"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
Reference in New Issue
Block a user