After read some posts here, i know that saga is an entity, and it has an uniqueId, and we should persist saga.
The question is why saga should has an uniqueId?
I think saga is just a business flow logic processor, like a state machine. And i agree that saga has status, but it seems that saga not must has an uniqueId.
Take OrderProcessor for example. OrderProcessor is a saga, and OrderProcessorFinder is a router which used to response the domain event or command, in the event handler or command handler method, it will find the correct OrderProcessor (a saga) by OrderId.
Maybe someone will said, for the ExpireRegistrationProcess (see the below code) command, to find the correct saga, we should use the saga Id. But my question is why we cannot also use the OrderId instead? In fact one order should always mapping to only one OrderProcessor, is that right?
So it seems that in no case we must use the saga id to locate the saga. Could anyone explain why we must have an saga Id?
The background why i ask this question is that recently i learned LMAX architecture, it has three key components: input events (like commands in CQRS), business logic processor (aggregate + saga), output events. And the whole business logic processor is working in memory. If we want to rebuild the whole business logic processor, the only way is replay all the input events (it is called event sourcing pattern). And when replaying all the input events, all the aggregates and sagas will be re-constructed and the status will be recovered to the lastest status. That means any saga is always constructed by responsing the aggregate output events. Suppose saga has its own id, and its value will be assigned when saga is instanciated. Like the following code:
public OrderProcessor()
{
this.Id = GuidUtil.NewSequentialId();
}
As you know, anytime when we create a new OrderProcessor, the Id of this OrderProcessor is different than any others. But the ExpireRegistrationProcess command is persisted in log, and the ProcessId of that command must be not equal with the new one, although their OrderId is same. So in this case if we replaying the command, we can never find the correct OrderProcessor again.
Below is the code of ExpireRegistrationProcess command:
public class ExpireRegistrationProcess : ICommand
{
public ExpireRegistrationProcess()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public Guid ProcessId { get; set; } //this is the OrderProcessorId
}
So, it seems that in LMAX, both aggregate and saga won't be persisted, all we persist is input events (commands). And if we want to rebuild the business logic processor, we just replay all the input events (commands). In fact, any saga is created after its associated aggregate, like OrderProcessor is created after Order created, so we can always use the OrderId as the saga id. What do you think?
Below is the code snippet of OrderProcessorFinder and OrderProcessor (saga):
public class OrderProcessorFinder :
IEventHandler<OrderPlaced>,
IEventHandler<OrderUpdated>,
IEnvelopedEventHandler<SeatsReserved>,
IEventHandler<PaymentCompleted>,
IEventHandler<OrderConfirmed>,
ICommandHandler<ExpireRegistrationProcess>
{
}
public class OrderProcessor
{
public void Handle(OrderPlaced message) {}
public void Handle(OrderUpdated message) {}
public void Handle(Envelope<SeatsReserved> envelope) {}
public void Handle(PaymentCompleted event) {}
public void Handle(OrderConfirmed event) {}
public void Handle(ExpireRegistrationProcess command) {}
}