Search code examples
c#publish-subscribesagarebus

ReBus equivalent to NServiceBus Saga ReplyToOriginator


I am working with Sagas in ReBus and from my experience with NServiceBus you could reply to the original creator of the Saga to give updates, something like this:

 Saga<>.ReplyToOriginator

I do not see an equivalent way of doing this with ReBus. Is there a way to do this, and if not what is a good pattern (other than the originator polling) I can use that will achieve the same thing? An example is trying to create a customer and the client wanting to know when it is created before trying to change it's address.

Here is a trivial example of the Customer scenario I quickly put together:

public class CreateCustomerSaga : Saga<CreateCustomerData>,
    IAmInitiatedBy<CreateCustomerCommand>,
    IHandleMessages<CustomerUniqunessCheckResult>
{
    private readonly IBus _bus;
    private readonly ICustomerResourceAccess _customerResourceAccess;

    public CreateCustomerSaga(IBus bus, ICustomerResourceAccess customerResourceAccess)
    {
        _bus = bus;
        _customerResourceAccess = customerResourceAccess;
    }

    public override void ConfigureHowToFindSaga()
    {
        Incoming<CustomerUniqunessCheckResult>(x => x.IsCustomerUnique).CorrelatesWith(y => y.CustomerId);
    }

    public void Handle(CreateCustomerCommand message)
    {
        Data.CustomerId = message.CustomerId;
        Data.CustomerName = message.CustomerName;

        _bus.Send(new CheckCustomerUniquenessCommand(message.CustomerId));
    }

    public void Handle(CustomerUniqunessCheckResult message)
    {
        if (message.IsCustomerUnique)
        {
            _customerResourceAccess.CreateCustomer(Data.CustomerId, Data.CustomerName);

            // This is what seems to be missing from ReBus to reply to the original sender
            _bus.?(new CustomerCreatedEvent(Data.CustomerId));
        }
        else
        {
            // This is what seems to be missing from ReBus to reply to the original sender
            _bus.?(new CustomerAlreadExistsEvent(Data.CustomerId));
        }
    }
}

public class CustomerCreatedEvent
{
    public Guid CustomerId { get; set; }

    public CustomerCreatedEvent(Guid customerId)
    {
        CustomerId = customerId;
    }
}

public class CustomerAlreadExistsEvent
{
    public Guid CustomerId { get; set; }

    public CustomerAlreadExistsEvent(Guid customerId)
    {
        CustomerId = customerId;
    }
}

public class CustomerUniqunessCheckResult
{
    public bool IsCustomerUnique { get; set; }
}

public class CheckCustomerUniquenessCommand
{
    public CheckCustomerUniquenessCommand(Guid customerId)
    { }
}

public interface ICustomerResourceAccess
{
    void CreateCustomer(Guid customerId, string customerName);
}

public class CreateCustomerCommand
{
    public Guid CustomerId { get; set; }

    public string CustomerName { get; set; }
}

public class CreateCustomerData : ISagaData
{
    public CreateCustomerData()
    {
        Id = Guid.NewGuid();
    }

    public Guid CustomerId { get; set; }

    public string CustomerName { get; set; }

    public Guid Id { get; set; }

    public int Revision { get; set; }
}

Solution

  • No, unfortunately there's no reply to originator function in Rebus' sagas at the moment. You can easily do it though, by storing the originator's endpoint when your saga is created like this (in all Handle methods of messages that can initiate the saga):

    if (IsNew) {
        Data.Originator = MessageContext.GetCurrent().ReturnAddress;
    }
    

    and then when you want to reply back to the originator:

    bus.Advanced.Routing.Send(Data.Originator, new HelloThereMyFriend());
    

    I've often thought about adding this to Rebus though, either as an extra field on ISagaData, or as an extra interface ISagaDataWithOriginator that you could optionally apply to your saga data, but I've never had the need (enough) myself.