开发者

Reusing NServiceBus messages saga data

Are there any reasons not to reuse an NServiceBus message (IMessage) in saga data? What I mean is having a message something like this:

public class Order : IMessage
{
   public virtual List<TillOrderLine> OrderLines { get; set; }
}

public class TillOrderLine : IMessage { ... }

And then also using it in the saga and saga data like this:

public class OrderProcessingSaga : 
    Saga<OrderProcessingSagaData>, 
    IAmStartedByMessages<Order> { ... }

public class OrderProcessingSagaData : ISagaEntity
{
   public virtual Guid Id { get; set; }
   public virtual string Originator { get; set; }
   public virtual string OriginalMessageId { get; set; }

   // The message data is stored by the saga here.
   public 开发者_开发技巧virtual Order Order { get; set; }
}

I realise that the message is stored by the transport layer (MSMQ), whilst the saga data is persisted to DB with the saga. It works for my current use case and seems more elegant to reuse the class rather than creating one for the message and another for saga storage.

I'm wondering if there are any gotchas with this approach?


I agree with everything mookid8000 said, however I have one more detail to add.

There can commonly be a lot of contention on your saga storage. To guarantee consistency, the saga storage provider will commonly place update locks on the data to guarantee that another message from the same saga can't mutate state at the same time.

If you are using the default NHibernate saga persister (and your virtual properties suggests to me that you are) then NHibernate uses some assumptions in order to save your data:

  • A complex type in your saga data will result in database columns following a ComplexTypePropName_ChildPropertyName syntax (or thereabouts - operating from memory here). So your saga data will still be contained within 1 row in one table.
  • If you have a list in your saga data, it must obviously create a new table to store the list items. Now your saga data is contained in one master row and multiple detail rows.
    • In order for NHibernate to map this, your collection type must also have a Guid propery named Id, meaning you can't store a list of primitives, and your TillOrderLine class (part of the public message) would need this Id property which makes no sense to the business purpose of your service.
  • I don't know what happens in your case if you have a complex type in your saga data (Order, basically the message reused) which itself contains a list (of TillOrderLines).

In any case, the more rows in the more tables the saga persister is required to split your data into, the harder it is to lock the involved rows with granular row locks. With enough contention, row locks will start to escalate to page locks and table locks and then you've really got a problem.

This is why a document database would really shine for saga storage, which is why in NServiceBus 3.0 they are making RavenDB the default saga persister.

Until then, take a look at the XML Serialization based Saga Persister I wrote, which can bring some of the benefits of the upcoming RavenDB-style saga persister to your application today.

If you must stick with NHibernate, I would strongly discourage you to couple your messages so tightly with your saga persistence solution. I almost guarantee it will come back to haunt you down the road.


If your surrent saga persister persists the data without complaining, I cannot think of anything wrong with this approach - as long as you're conscious that you're reusing a class for two purposes, thus introducing a coupling between saga data (which is private to the service) and messages (which are inherently public) regarding fields present, naming, etc.

Then you can build classes for all of your saga data if and when you need to deviate from the ways your messages are structured.

PS: TillOrderLine doesn't need to be marked as IMessage if its only purpose is to be aggregated inside Order.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜