github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/cqrs/event_processor.go (about) 1 package cqrs 2 3 import ( 4 "fmt" 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 EventProcessorConfig struct { 14 // GenerateSubscribeTopic is used to generate topic for subscribing to events. 15 // If event processor is using handler groups, GenerateSubscribeTopic is used instead. 16 GenerateSubscribeTopic EventProcessorGenerateSubscribeTopicFn 17 18 // SubscriberConstructor is used to create subscriber for EventHandler. 19 // 20 // This function is called for every EventHandler instance. 21 // If you want to re-use one subscriber for multiple handlers, use GroupEventProcessor instead. 22 SubscriberConstructor EventProcessorSubscriberConstructorFn 23 24 // OnHandle is called before handling event. 25 // OnHandle works in a similar way to middlewares: 26 // you can inject additional logic before and after handling a event. 27 // 28 // Because of that, you need to explicitly call params.Handler.Handle() to handle the event. 29 // 30 // func(params EventProcessorOnHandleParams) (err error) { 31 // // logic before handle 32 // // (...) 33 // 34 // err := params.Handler.Handle(params.Message.Context(), params.Event) 35 // 36 // // logic after handle 37 // // (...) 38 // 39 // return err 40 // } 41 // 42 // This option is not required. 43 OnHandle EventProcessorOnHandleFn 44 45 // AckOnUnknownEvent is used to decide if message should be acked if event has no handler defined. 46 AckOnUnknownEvent bool 47 48 // Marshaler is used to marshal and unmarshal events. 49 // It is required. 50 Marshaler CommandEventMarshaler 51 52 // Logger instance used to log. 53 // If not provided, watermill.NopLogger is used. 54 Logger watermill.LoggerAdapter 55 56 // disableRouterAutoAddHandlers is used to keep backwards compatibility. 57 // it is set when EventProcessor is created by NewEventProcessor. 58 // Deprecated: please migrate to NewEventProcessorWithConfig. 59 disableRouterAutoAddHandlers bool 60 } 61 62 func (c *EventProcessorConfig) setDefaults() { 63 if c.Logger == nil { 64 c.Logger = watermill.NopLogger{} 65 } 66 } 67 68 func (c EventProcessorConfig) Validate() error { 69 var err error 70 71 if c.Marshaler == nil { 72 err = multierr.Append(err, errors.New("missing Marshaler")) 73 } 74 75 if c.GenerateSubscribeTopic == nil { 76 err = multierr.Append(err, errors.New("missing GenerateHandlerTopic")) 77 } 78 if c.SubscriberConstructor == nil { 79 err = multierr.Append(err, errors.New("missing SubscriberConstructor")) 80 } 81 82 return err 83 } 84 85 type EventProcessorGenerateSubscribeTopicFn func(EventProcessorGenerateSubscribeTopicParams) (string, error) 86 87 type EventProcessorGenerateSubscribeTopicParams struct { 88 EventName string 89 EventHandler EventHandler 90 } 91 92 type EventProcessorSubscriberConstructorFn func(EventProcessorSubscriberConstructorParams) (message.Subscriber, error) 93 94 type EventProcessorSubscriberConstructorParams struct { 95 HandlerName string 96 EventHandler EventHandler 97 } 98 99 type EventProcessorOnHandleFn func(params EventProcessorOnHandleParams) error 100 101 type EventProcessorOnHandleParams struct { 102 Handler EventHandler 103 104 Event any 105 EventName string 106 107 // Message is never nil and can be modified. 108 Message *message.Message 109 } 110 111 // EventProcessor determines which EventHandler should handle event received from event bus. 112 type EventProcessor struct { 113 router *message.Router 114 handlers []EventHandler 115 config EventProcessorConfig 116 } 117 118 // NewEventProcessorWithConfig creates a new EventProcessor. 119 func NewEventProcessorWithConfig(router *message.Router, config EventProcessorConfig) (*EventProcessor, error) { 120 config.setDefaults() 121 122 if err := config.Validate(); err != nil { 123 return nil, errors.Wrap(err, "invalid config EventProcessor") 124 } 125 if router == nil && !config.disableRouterAutoAddHandlers { 126 return nil, errors.New("missing router") 127 } 128 129 return &EventProcessor{ 130 router: router, 131 config: config, 132 }, nil 133 } 134 135 // NewEventProcessor creates a new EventProcessor. 136 // Deprecated. Use NewEventProcessorWithConfig instead. 137 func NewEventProcessor( 138 individualHandlers []EventHandler, 139 generateTopic func(eventName string) string, 140 subscriberConstructor EventsSubscriberConstructor, 141 marshaler CommandEventMarshaler, 142 logger watermill.LoggerAdapter, 143 ) (*EventProcessor, error) { 144 if len(individualHandlers) == 0 { 145 return nil, errors.New("missing handlers") 146 } 147 if generateTopic == nil { 148 return nil, errors.New("nil generateTopic") 149 } 150 if subscriberConstructor == nil { 151 return nil, errors.New("missing subscriberConstructor") 152 } 153 if marshaler == nil { 154 return nil, errors.New("missing marshaler") 155 } 156 if logger == nil { 157 logger = watermill.NopLogger{} 158 } 159 160 eventProcessorConfig := EventProcessorConfig{ 161 AckOnUnknownEvent: true, // this is the previous default behaviour - keeping backwards compatibility 162 GenerateSubscribeTopic: func(params EventProcessorGenerateSubscribeTopicParams) (string, error) { 163 return generateTopic(params.EventName), nil 164 }, 165 SubscriberConstructor: func(params EventProcessorSubscriberConstructorParams) (message.Subscriber, error) { 166 return subscriberConstructor(params.HandlerName) 167 }, 168 Marshaler: marshaler, 169 Logger: logger, 170 disableRouterAutoAddHandlers: true, 171 } 172 eventProcessorConfig.setDefaults() 173 174 ep, err := NewEventProcessorWithConfig(nil, eventProcessorConfig) 175 if err != nil { 176 return nil, err 177 } 178 179 for _, handler := range individualHandlers { 180 if err := ep.AddHandlers(handler); err != nil { 181 return nil, err 182 } 183 } 184 185 return ep, nil 186 } 187 188 // EventsSubscriberConstructor creates a subscriber for EventHandler. 189 // It allows you to create separated customized Subscriber for every command handler. 190 // 191 // When handler groups are used, handler group is passed as handlerName. 192 // Deprecated: please use EventProcessorSubscriberConstructorFn instead. 193 type EventsSubscriberConstructor func(handlerName string) (message.Subscriber, error) 194 195 // AddHandlers adds a new EventHandler to the EventProcessor and adds it to the router. 196 func (p *EventProcessor) AddHandlers(handlers ...EventHandler) error { 197 if p.config.disableRouterAutoAddHandlers { 198 p.handlers = append(p.handlers, handlers...) 199 return nil 200 } 201 202 for _, handler := range handlers { 203 if err := p.addHandlerToRouter(p.router, handler); err != nil { 204 return err 205 } 206 207 p.handlers = append(p.handlers, handler) 208 } 209 210 return nil 211 } 212 213 // AddHandlersToRouter adds the EventProcessor's handlers to the given router. 214 // It should be called only once per EventProcessor instance. 215 // 216 // It is required to call AddHandlersToRouter only if command processor 217 // is created with NewEventProcessor (disableRouterAutoAddHandlers is set to true). 218 // Deprecated: please migrate to event processor created by NewEventProcessorWithConfig. 219 func (p EventProcessor) AddHandlersToRouter(r *message.Router) error { 220 if !p.config.disableRouterAutoAddHandlers { 221 return errors.New("AddHandlersToRouter should be called only when using deprecated NewEventProcessor") 222 } 223 224 for i := range p.handlers { 225 handler := p.handlers[i] 226 227 if err := p.addHandlerToRouter(r, handler); err != nil { 228 return err 229 } 230 } 231 232 return nil 233 } 234 235 func (p EventProcessor) addHandlerToRouter(r *message.Router, handler EventHandler) error { 236 if err := validateEvent(handler.NewEvent()); err != nil { 237 return errors.Wrapf(err, "invalid event for handler %s", handler.HandlerName()) 238 } 239 240 handlerName := handler.HandlerName() 241 eventName := p.config.Marshaler.Name(handler.NewEvent()) 242 243 topicName, err := p.config.GenerateSubscribeTopic(EventProcessorGenerateSubscribeTopicParams{ 244 EventName: eventName, 245 EventHandler: handler, 246 }) 247 if err != nil { 248 return errors.Wrapf(err, "cannot generate topic name for handler %s", handlerName) 249 } 250 251 logger := p.config.Logger.With(watermill.LogFields{ 252 "event_handler_name": handlerName, 253 "topic": topicName, 254 }) 255 256 handlerFunc, err := p.routerHandlerFunc(handler, logger) 257 if err != nil { 258 return err 259 } 260 261 if p.config.SubscriberConstructor == nil { 262 return errors.New("missing SubscriberConstructor config option") 263 } 264 265 subscriber, err := p.config.SubscriberConstructor(EventProcessorSubscriberConstructorParams{ 266 HandlerName: handlerName, 267 EventHandler: handler, 268 }) 269 if err != nil { 270 return errors.Wrap(err, "cannot create subscriber for event processor") 271 } 272 273 if err := addHandlerToRouter(p.config.Logger, r, handlerName, topicName, handlerFunc, subscriber); err != nil { 274 return err 275 } 276 277 return nil 278 } 279 280 func (p EventProcessor) Handlers() []EventHandler { 281 return p.handlers 282 } 283 284 func addHandlerToRouter(logger watermill.LoggerAdapter, r *message.Router, handlerName string, topicName string, 285 handlerFunc message.NoPublishHandlerFunc, subscriber message.Subscriber) error { 286 logger = logger.With(watermill.LogFields{ 287 "event_handler_name": handlerName, 288 "topic": topicName, 289 }) 290 291 logger.Debug("Adding CQRS event handler to router", nil) 292 293 r.AddNoPublisherHandler( 294 handlerName, 295 topicName, 296 subscriber, 297 handlerFunc, 298 ) 299 300 return nil 301 } 302 303 func (p EventProcessor) routerHandlerFunc(handler EventHandler, 304 logger watermill.LoggerAdapter) (message.NoPublishHandlerFunc, error) { 305 initEvent := handler.NewEvent() 306 expectedEventName := p.config.Marshaler.Name(initEvent) 307 308 if err := validateEvent(initEvent); err != nil { 309 return nil, err 310 } 311 312 return func(msg *message.Message) error { 313 event := handler.NewEvent() 314 messageEventName := p.config.Marshaler.NameFromMessage(msg) 315 316 if messageEventName != expectedEventName { 317 if !p.config.AckOnUnknownEvent { 318 return fmt.Errorf("received unexpected event type %s, expected %s", messageEventName, expectedEventName) 319 } else { 320 logger.Trace("Received different event type than expected, ignoring", watermill.LogFields{ 321 "message_uuid": msg.UUID, 322 "expected_event_type": expectedEventName, 323 "received_event_type": messageEventName, 324 }) 325 return nil 326 } 327 } 328 329 logger.Debug("Handling event", watermill.LogFields{ 330 "message_uuid": msg.UUID, 331 "received_event_type": messageEventName, 332 }) 333 334 ctx := CtxWithOriginalMessage(msg.Context(), msg) 335 msg.SetContext(ctx) 336 337 if err := p.config.Marshaler.Unmarshal(msg, event); err != nil { 338 return err 339 } 340 341 handle := func(params EventProcessorOnHandleParams) error { 342 return params.Handler.Handle(ctx, params.Event) 343 } 344 if p.config.OnHandle != nil { 345 handle = p.config.OnHandle 346 } 347 348 err := handle(EventProcessorOnHandleParams{ 349 Handler: handler, 350 Event: event, 351 EventName: messageEventName, 352 Message: msg, 353 }) 354 if err != nil { 355 logger.Debug("Error when handling event", watermill.LogFields{"err": err}) 356 return err 357 } 358 359 return nil 360 }, nil 361 } 362 363 func validateEvent(event any) error { 364 // EventHandler's NewEvent must return a pointer, because it is used to unmarshal 365 if err := isPointer(event); err != nil { 366 return errors.Wrap(err, "command must be a non-nil pointer") 367 } 368 369 return nil 370 }