Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Last active July 12, 2018 09:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramonsmits/75a80ec9a45f8042bc4f to your computer and use it in GitHub Desktop.
Save ramonsmits/75a80ec9a45f8042bc4f to your computer and use it in GitHub Desktop.
NServiceBus buffering saga example
public class CustomerNotificationHandler
: IHandleMessage<CustomerOrderNotification>
, IHandleMessage<CustomerOrderStatusNotification>
{
public IBus Bus {get;set;}
INotificationService GetClientForCustomer(string customerId)
{
return null; // Create a api client for your customer
}
public void Handle(CustomerOrderNotification message)
{
var service = GetClientForCustomer(message.CustomerId);
service.Notifiy(message);
Bus.Reply(new Notified());
}
public void Handle(CustomerOrderStatusNotification message)
{
var service = GetClientForCustomer(message.CustomerId);
service.Notifiy(message);
Bus.Reply(new Notified());
}
}
// Based on saga data for message buffer
public class CustomerOrderNotificationSaga<CustomerOrderNotificationSaga.Data>
: IAmStartedByMessages<OrderCreated>
, IHandleMessages<OrderStatusChanged>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<CustomerOrderNotificationSaga.Data> mapper)
{
// Mapping for OrderCreated is not required, as it is started by it.
mapper.ConfigureMapping<OrderStatusChanged>(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId);
}
public void Handle(OrderCreated message)
{
Data.OrderId = message.OrderId;
Bus.Send(new CustomerOrderNotification{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
// Other order info?
});
}
public void Handle(OrderStatusChanged message)
{
var instance = new CustomerOrderStatusNotification
{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
Status = message.Status,
Version = message.Version, // <-- Provides ordering!
};
Notifications.Add(instance);
Next();
}
public void Handle(Notified message)
{
Data.OrderNr++; // <-- Increase version
if(Notifications.Count>0)
{
Next();
}
}
void Next()
{
CustomerOrderStatusNotification instance = notifications
.OrderBy(x=>x.Version)
.First();
if(Data.OrderNr != instance.Version)
{
return; // Versions are not equal!
}
notifications.Remove(instance);
Bus.Send(instance);
}
public class Data
{
[Unique]
public virtual string OrderId {get;set;}
public virtual ICollection<CustomerOrderStatusNotification> Notifications {get;set;}
public virtual int OrderNr {get;set;}
}
}
// Based on retry logic
public class CustomerOrderNotificationSaga<CustomerOrderNotificationSaga.Data>
: IAmStartedByMessages<OrderCreated>
, IHandleMessages<OrderStatusChanged>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<CustomerOrderNotificationSaga.Data> mapper)
{
// Mapping for OrderCreated is not required, as it is started by it.
mapper.ConfigureMapping<OrderStatusChanged>(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId);
}
public void Handle(OrderCreated message)
{
Data.OrderId = message.OrderId;
Bus.Send(new CustomerOrderNotification{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
// Other order info?
});
}
public void Handle(OrderStatusChanged message)
{
if(Data.OrderNr == 0)
{
throw new InvalidOperation("Version out of order, order delivery not yet confirmed.");
}
var instance = new CustomerOrderStatusNotification
{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
Status = message.Status,
Timestamp = message.Timestamp
};
Bus.Send(instance);
}
public void Handle(Notified message)
{
Data.OrderNr++;
}
public class Data : SagaData
{
[Unique]
public virtual string OrderId {get;set;}
public virtual int OrderNr {get;set;}
}
}
// Based on retry logic
public class CustomerOrderNotificationSaga<CustomerOrderNotificationSaga.Data>
: IAmStartedByMessages<OrderCreated>
, IHandleMessages<OrderStatusChanged>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<CustomerOrderNotificationSaga.Data> mapper)
{
// Mapping for OrderCreated is not required, as it is started by it.
mapper.ConfigureMapping<OrderStatusChanged>(message => message.OrderId).ToSaga(sagaData => sagaData.OrderId);
}
public void Handle(OrderCreated message)
{
Data.OrderId = message.OrderId;
Bus.Send(new CustomerOrderNotification{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
// Other order info?
});
}
public void Handle(OrderStatusChanged message)
{
if(Data.OrderNr != message.Version)
{
throw new InvalidOperation("Version out of order.");
}
var instance = new CustomerOrderStatusNotification
{
CustomerId = message.CustomerId,
OrderId = message.OrderId,
Status = message.Status,
Version = message.Version, // <-- Provides ordering!
};
Bus.Send(instance);
}
public void Handle(Notified message)
{
Data.OrderNr++;
}
public class Data : SagaData
{
[Unique]
public virtual bool OrderId {get;set;}
public virtual int OrderNr {get;set;}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment