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  }