github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/cqrs/command_bus.go (about) 1 package cqrs 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 "go.uber.org/multierr" 8 9 "github.com/wfusion/gofusion/common/infra/watermill" 10 "github.com/wfusion/gofusion/common/infra/watermill/message" 11 ) 12 13 type CommandBusConfig struct { 14 // GeneratePublishTopic is used to generate topic for publishing command. 15 GeneratePublishTopic CommandBusGeneratePublishTopicFn 16 17 // OnSend is called before publishing the command. 18 // The *message.Message can be modified. 19 // 20 // This option is not required. 21 OnSend CommandBusOnSendFn 22 23 // Marshaler is used to marshal and unmarshal commands. 24 // It is required. 25 Marshaler CommandEventMarshaler 26 27 // Logger instance used to log. 28 // If not provided, watermill.NopLogger is used. 29 Logger watermill.LoggerAdapter 30 } 31 32 func (c *CommandBusConfig) setDefaults() { 33 if c.Logger == nil { 34 c.Logger = watermill.NopLogger{} 35 } 36 } 37 38 func (c CommandBusConfig) Validate() error { 39 var err error 40 41 if c.Marshaler == nil { 42 err = multierr.Append(err, errors.New("missing Marshaler")) 43 } 44 45 if c.GeneratePublishTopic == nil { 46 err = multierr.Append(err, errors.New("missing GeneratePublishTopic")) 47 } 48 49 return err 50 } 51 52 type CommandBusGeneratePublishTopicFn func(CommandBusGeneratePublishTopicParams) (string, error) 53 54 type CommandBusGeneratePublishTopicParams struct { 55 CommandName string 56 Command any 57 } 58 59 type CommandBusOnSendFn func(params CommandBusOnSendParams) error 60 61 type CommandBusOnSendParams struct { 62 CommandName string 63 Command any 64 65 // Message is never nil and can be modified. 66 Message *message.Message 67 } 68 69 // CommandBus transports commands to command handlers. 70 type CommandBus struct { 71 publisher message.Publisher 72 73 config CommandBusConfig 74 } 75 76 // NewCommandBusWithConfig creates a new CommandBus. 77 func NewCommandBusWithConfig(publisher message.Publisher, config CommandBusConfig) (*CommandBus, error) { 78 if publisher == nil { 79 return nil, errors.New("missing publisher") 80 } 81 82 config.setDefaults() 83 if err := config.Validate(); err != nil { 84 return nil, errors.Wrap(err, "invalid config") 85 } 86 87 return &CommandBus{publisher, config}, nil 88 } 89 90 // NewCommandBus creates a new CommandBus. 91 // Deprecated: use NewCommandBusWithConfig instead. 92 func NewCommandBus( 93 publisher message.Publisher, 94 generateTopic func(commandName string) string, 95 marshaler CommandEventMarshaler, 96 ) (*CommandBus, error) { 97 if publisher == nil { 98 return nil, errors.New("missing publisher") 99 } 100 if generateTopic == nil { 101 return nil, errors.New("missing generateTopic") 102 } 103 if marshaler == nil { 104 return nil, errors.New("missing marshaler") 105 } 106 107 return &CommandBus{publisher, CommandBusConfig{ 108 GeneratePublishTopic: func(params CommandBusGeneratePublishTopicParams) (string, error) { 109 return generateTopic(params.CommandName), nil 110 }, 111 Marshaler: marshaler, 112 }}, nil 113 } 114 115 // Send sends command to the command bus. 116 func (c CommandBus) Send(ctx context.Context, cmd any) error { 117 return c.SendWithModifiedMessage(ctx, cmd, nil) 118 } 119 120 func (c CommandBus) SendWithModifiedMessage(ctx context.Context, cmd any, modify func(*message.Message) error) error { 121 msg, topicName, err := c.newMessage(ctx, cmd) 122 if err != nil { 123 return err 124 } 125 126 if modify != nil { 127 if err := modify(msg); err != nil { 128 return errors.Wrap(err, "cannot modify message") 129 } 130 } 131 132 if err := c.publisher.Publish(ctx, topicName, msg); err != nil { 133 return err 134 } 135 136 return nil 137 } 138 139 func (c CommandBus) newMessage(ctx context.Context, command any) (*message.Message, string, error) { 140 msg, err := c.config.Marshaler.Marshal(command) 141 if err != nil { 142 return nil, "", err 143 } 144 145 commandName := c.config.Marshaler.Name(command) 146 topicName, err := c.config.GeneratePublishTopic(CommandBusGeneratePublishTopicParams{ 147 CommandName: commandName, 148 Command: command, 149 }) 150 if err != nil { 151 return nil, "", errors.Wrap(err, "cannot generate topic name") 152 } 153 154 msg.SetContext(ctx) 155 156 if c.config.OnSend != nil { 157 err := c.config.OnSend(CommandBusOnSendParams{ 158 CommandName: commandName, 159 Command: command, 160 Message: msg, 161 }) 162 if err != nil { 163 return nil, "", errors.Wrap(err, "cannot execute OnSend") 164 } 165 } 166 167 return msg, topicName, nil 168 }