github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/requestreply/handler.go (about) 1 package requestreply 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 8 "github.com/wfusion/gofusion/common/infra/watermill/components/cqrs" 9 "github.com/wfusion/gofusion/common/infra/watermill/message" 10 ) 11 12 // NewCommandHandler creates a new CommandHandler which supports the request-reply pattern. 13 // The result handler is handler compatible with cqrs.CommandHandler. 14 // 15 // The logic if a command should be acked or not is based on the logic of the Backend. 16 // For example, for the PubSubBackend, it depends on the `PubSubBackendConfig.AckCommandErrors` option. 17 func NewCommandHandler[Command any]( 18 handlerName string, 19 backend Backend[struct{}], 20 handleFunc func(ctx context.Context, cmd *Command) error, 21 ) cqrs.CommandHandler { 22 return cqrs.NewCommandHandler(handlerName, func(ctx context.Context, cmd *Command) error { 23 handlerErr := handleFunc(ctx, cmd) 24 25 originalMessage, err := originalCommandMsgFromCtx(ctx) 26 if err != nil { 27 return err 28 } 29 30 return backend.OnCommandProcessed(ctx, BackendOnCommandProcessedParams[struct{}]{ 31 Command: cmd, 32 CommandMessage: originalMessage, 33 HandleErr: handlerErr, 34 }) 35 }) 36 } 37 38 // NewCommandHandlerWithResult creates a new CommandHandler which supports the request-reply pattern with a result. 39 // The result handler is handler compatible with cqrs.CommandHandler. 40 // 41 // In addition to cqrs.CommandHandler, it also allows returninga result from the handler. 42 // The result is passed to the Backend implementation and sent to the caller. 43 // 44 // The logic if a command should be acked or not is based on the logic of the Backend. 45 // For example, for the PubSubBackend, it depends on the `PubSubBackendConfig.AckCommandErrors` option. 46 // 47 // The reply is sent to the caller, even if the handler returns an error. 48 func NewCommandHandlerWithResult[Command any, Result any]( 49 handlerName string, 50 backend Backend[Result], 51 handleFunc func(ctx context.Context, cmd *Command) (Result, error), 52 ) cqrs.CommandHandler { 53 return cqrs.NewCommandHandler(handlerName, func(ctx context.Context, cmd *Command) error { 54 resp, handlerErr := handleFunc(ctx, cmd) 55 56 originalMessage, err := originalCommandMsgFromCtx(ctx) 57 if err != nil { 58 return err 59 } 60 61 return backend.OnCommandProcessed(ctx, BackendOnCommandProcessedParams[Result]{ 62 Command: cmd, 63 CommandMessage: originalMessage, 64 HandlerResult: resp, 65 HandleErr: handlerErr, 66 }) 67 }) 68 } 69 70 func originalCommandMsgFromCtx(ctx context.Context) (*message.Message, error) { 71 originalMessage := cqrs.OriginalMessageFromCtx(ctx) 72 if originalMessage == nil { 73 // This should not happen, as long as cqrs.CommandProcessor is used - but it's not mandatory. 74 // In this case, it's enough to use cqrs.CtxWithOriginalMessage 75 return nil, errors.New( 76 "original message not found in context, did you pass context correctly everywhere? " + 77 "did you use cqrs.CommandProcessor? " + 78 "if you are using custom implementation, please call cqrs.CtxWithOriginalMessage on the context passed to the handler", 79 ) 80 } 81 return originalMessage, nil 82 }