rebus-org / Rebus

:bus: Simple and lean service bus implementation for .NET
https://mookid.dk/category/rebus
Other
2.26k stars 354 forks source link

Saga Pattern with Rebus #1170

Closed AhmedElbatt closed 1 week ago

AhmedElbatt commented 1 week ago

Regarding the following code; I am running Saga pattern to execute distributed transaction for each payment in the list; Because the execution of Rebus is asynchronus I receive the result directly even all saga instances are not finished. My Question is; how can I make the code waiting till all saga instances are completed and then return the result ?

public class CreatePrelimnaryMoneyTransfersCommandHandler : IRequestHandler<CreatePrelimnaryMoneyTransfersCommand, Result<bool>>
{
    private readonly IBus _bus;
    private readonly IPaymentRepository _paymentRepository;

    public CreatePrelimnaryMoneyTransfersCommandHandler(IBus bus, IPaymentRepository paymentRepository)
    {
        _bus = bus;
        _paymentRepository = paymentRepository;
    }

    public async Task<Result<bool>> Handle(CreatePrelimnaryMoneyTransfersCommand request, CancellationToken cancellationToken)
    {
        try
        {
            var payments = await _paymentRepository.GetPaymentsByQueryParameters(new PaymentQueryParameters { ShowFailedOrChargedBackWithNoPayoutId = true });

            foreach (var payment in payments)
            {
                await _bus.Send(new ExecuteCreatePrelimnaryMoneyTransfersSaga(payment.PaymentId));
            }
            // this code should be waited until all payments are handled
            return new Result<bool>(true, string.Empty);
        }
        catch (Exception ex)
        {
            // Handle or log the exception as needed
            return new Result<bool>(false, ex.Message);
        }
    }
}
mookid8000 commented 1 week ago

If I understand correctly, your ExecuteCreatePrelimnaryMoneyTransfersSaga command initiates a saga, which then carries out the steps involved in making one payment, right?

One way of coordinating this, would be to make the logic of initiating the payments and correlating the results of them a saga too – this way you can "do stuff" when all the payment results have been collected.

It will of course have the consequence that your CreatePrelimnaryMoneyTransfersCommandHandler request handler will not be able to return a result immediately to the caller, because all the logic will execute asynchronously in the background. This is usually something that needs to be worked around by coming up with a design that embraces the asynchronous nature of the background processing e.g. by having UI that properly reflects that stuff is going on and waiting for a conclusion, possibly accompanied by some kind of notification mechanism to let the user know that processing is completed.

I hope that sounds sensible to you 🙂