github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/cqrs/event_processor_group.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 EventGroupProcessorConfig struct { 14 // GenerateSubscribeTopic is used to generate topic for subscribing to events for handler groups. 15 // This option is required for EventProcessor if handler groups are used. 16 GenerateSubscribeTopic EventGroupProcessorGenerateSubscribeTopicFn 17 18 // SubscriberConstructor is used to create subscriber for GroupEventHandler. 19 // This function is called for every events group once - thanks to that it's possible to have one subscription per group. 20 // It's useful, when we are processing events from one stream and we want to do it in order. 21 SubscriberConstructor EventGroupProcessorSubscriberConstructorFn 22 23 // OnHandle is called before handling event. 24 // OnHandle works in a similar way to middlewares: you can inject additional logic before and after handling a event. 25 // 26 // Because of that, you need to explicitly call params.Handler.Handle() to handle the event. 27 // 28 // func(params EventGroupProcessorOnHandleParams) (err error) { 29 // // logic before handle 30 // // (...) 31 // 32 // err := params.Handler.Handle(params.Message.Context(), params.Event) 33 // 34 // // logic after handle 35 // // (...) 36 // 37 // return err 38 // } 39 // 40 // This option is not required. 41 OnHandle EventGroupProcessorOnHandleFn 42 43 // AckOnUnknownEvent is used to decide if message should be acked if event has no handler defined. 44 AckOnUnknownEvent bool 45 46 // Marshaler is used to marshal and unmarshal events. 47 // It is required. 48 Marshaler CommandEventMarshaler 49 50 // Logger instance used to log. 51 // If not provided, watermill.NopLogger is used. 52 Logger watermill.LoggerAdapter 53 } 54 55 func (c *EventGroupProcessorConfig) setDefaults() { 56 if c.Logger == nil { 57 c.Logger = watermill.NopLogger{} 58 } 59 } 60 61 func (c EventGroupProcessorConfig) Validate() error { 62 var err error 63 64 if c.Marshaler == nil { 65 err = multierr.Append(err, errors.New("missing Marshaler")) 66 } 67 68 if c.GenerateSubscribeTopic == nil { 69 err = multierr.Append(err, errors.New("missing GenerateHandlerGroupTopic")) 70 } 71 if c.SubscriberConstructor == nil { 72 err = multierr.Append(err, errors.New("missing SubscriberConstructor")) 73 } 74 75 return err 76 } 77 78 type EventGroupProcessorGenerateSubscribeTopicFn func(EventGroupProcessorGenerateSubscribeTopicParams) (string, error) 79 80 type EventGroupProcessorGenerateSubscribeTopicParams struct { 81 EventGroupName string 82 EventGroupHandlers []GroupEventHandler 83 } 84 85 type EventGroupProcessorSubscriberConstructorFn func(EventGroupProcessorSubscriberConstructorParams) (message.Subscriber, error) 86 87 type EventGroupProcessorSubscriberConstructorParams struct { 88 EventGroupName string 89 EventGroupHandlers []GroupEventHandler 90 } 91 92 type EventGroupProcessorOnHandleFn func(params EventGroupProcessorOnHandleParams) error 93 94 type EventGroupProcessorOnHandleParams struct { 95 GroupName string 96 Handler GroupEventHandler 97 98 Event any 99 EventName string 100 101 // Message is never nil and can be modified. 102 Message *message.Message 103 } 104 105 // EventGroupProcessor determines which EventHandler should handle event received from event bus. 106 // Compared to EventProcessor, EventGroupProcessor allows to have multiple handlers that share the same subscriber instance. 107 type EventGroupProcessor struct { 108 router *message.Router 109 110 groupEventHandlers map[string][]GroupEventHandler 111 112 config EventGroupProcessorConfig 113 } 114 115 // NewEventGroupProcessorWithConfig creates a new EventGroupProcessor. 116 func NewEventGroupProcessorWithConfig(router *message.Router, config EventGroupProcessorConfig) (*EventGroupProcessor, error) { 117 config.setDefaults() 118 119 if err := config.Validate(); err != nil { 120 return nil, errors.Wrap(err, "invalid config EventProcessor") 121 } 122 if router == nil { 123 return nil, errors.New("missing router") 124 } 125 126 return &EventGroupProcessor{ 127 router: router, 128 groupEventHandlers: map[string][]GroupEventHandler{}, 129 config: config, 130 }, nil 131 } 132 133 // AddHandlersGroup adds a new list of GroupEventHandler to the EventGroupProcessor and adds it to the router. 134 // 135 // Compared to AddHandlers, AddHandlersGroup allows to have multiple handlers that share the same subscriber instance. 136 // 137 // Handlers group needs to be unique within the EventProcessor instance. 138 // 139 // Handler group name is used as handler's name in router. 140 func (p *EventGroupProcessor) AddHandlersGroup(groupName string, handlers ...GroupEventHandler) error { 141 if len(handlers) == 0 { 142 return errors.New("no handlers provided") 143 } 144 if _, ok := p.groupEventHandlers[groupName]; ok { 145 return fmt.Errorf("event handler group '%s' already exists", groupName) 146 } 147 148 if err := p.addHandlerToRouter(p.router, groupName, handlers); err != nil { 149 return err 150 } 151 152 p.groupEventHandlers[groupName] = handlers 153 154 return nil 155 } 156 157 func (p EventGroupProcessor) addHandlerToRouter(r *message.Router, groupName string, handlersGroup []GroupEventHandler) error { 158 for i, handler := range handlersGroup { 159 if err := validateEvent(handler.NewEvent()); err != nil { 160 return errors.Wrapf( 161 err, 162 "invalid event for handler %T (num %d) in group %s", 163 handler, 164 i, 165 groupName, 166 ) 167 } 168 } 169 170 topicName, err := p.config.GenerateSubscribeTopic(EventGroupProcessorGenerateSubscribeTopicParams{ 171 EventGroupName: groupName, 172 EventGroupHandlers: handlersGroup, 173 }) 174 if err != nil { 175 return errors.Wrapf(err, "cannot generate topic name for handler group %s", groupName) 176 } 177 178 logger := p.config.Logger.With(watermill.LogFields{ 179 "event_handler_group_name": groupName, 180 "topic": topicName, 181 }) 182 183 handlerFunc, err := p.routerHandlerGroupFunc(handlersGroup, groupName, logger) 184 if err != nil { 185 return err 186 } 187 188 subscriber, err := p.config.SubscriberConstructor(EventGroupProcessorSubscriberConstructorParams{ 189 EventGroupName: groupName, 190 EventGroupHandlers: handlersGroup, 191 }) 192 if err != nil { 193 return errors.Wrap(err, "cannot create subscriber for event processor") 194 } 195 196 if err := addHandlerToRouter(p.config.Logger, r, groupName, topicName, handlerFunc, subscriber); err != nil { 197 return err 198 } 199 200 return nil 201 } 202 203 func (p EventGroupProcessor) routerHandlerGroupFunc(handlers []GroupEventHandler, groupName string, logger watermill.LoggerAdapter) (message.NoPublishHandlerFunc, error) { 204 return func(msg *message.Message) error { 205 messageEventName := p.config.Marshaler.NameFromMessage(msg) 206 207 for _, handler := range handlers { 208 initEvent := handler.NewEvent() 209 expectedEventName := p.config.Marshaler.Name(initEvent) 210 211 event := handler.NewEvent() 212 213 if messageEventName != expectedEventName { 214 logger.Trace("Received different event type than expected, ignoring", watermill.LogFields{ 215 "message_uuid": msg.UUID, 216 "expected_event_type": expectedEventName, 217 "received_event_type": messageEventName, 218 }) 219 continue 220 } 221 222 logger.Debug("Handling event", watermill.LogFields{ 223 "message_uuid": msg.UUID, 224 "received_event_type": messageEventName, 225 }) 226 227 ctx := CtxWithOriginalMessage(msg.Context(), msg) 228 msg.SetContext(ctx) 229 230 if err := p.config.Marshaler.Unmarshal(msg, event); err != nil { 231 return err 232 } 233 234 handle := func(params EventGroupProcessorOnHandleParams) error { 235 return params.Handler.Handle(ctx, params.Event) 236 } 237 if p.config.OnHandle != nil { 238 handle = p.config.OnHandle 239 } 240 241 err := handle(EventGroupProcessorOnHandleParams{ 242 GroupName: groupName, 243 Handler: handler, 244 EventName: messageEventName, 245 Event: event, 246 Message: msg, 247 }) 248 if err != nil { 249 logger.Debug("Error when handling event", watermill.LogFields{"err": err}) 250 return err 251 } 252 253 return nil 254 } 255 256 if !p.config.AckOnUnknownEvent { 257 return fmt.Errorf("no handler found for event %s", p.config.Marshaler.NameFromMessage(msg)) 258 } else { 259 logger.Trace("Received event can't be handled by any handler in handler group", watermill.LogFields{ 260 "message_uuid": msg.UUID, 261 "received_event_type": messageEventName, 262 }) 263 return nil 264 } 265 }, nil 266 }