On multiple sites (e.g. here or here Sagas are described as a mechanism that listens to domain events and reacts to them, executing new commands, and finally modifying the domain, etc.
Is there any difference between a Saga and a simple event dispatcher, where you have some subscribers react to events?
Saga pattern can help us maintain the data consistency among the microservices architecture efficiently. Event Sourcing ensures that all changes to business entity's state are stored as a sequence of events.
A Saga is a long running process that triggers by events outside the domain. That events could happen in seconds, minutes or days. The difference with simple event bus is that a Saga keeps a state machine that can be persisted to handle long running process in a "disconnected" workflow due to the external events.
The SAGA pattern is a failure management pattern that brings consistency in distributed applications and coordinates transactions between various microservices to maintain consistency. Command and Query Responsibility Segregation or CQRS is a pattern that separates read and update operations for a data store.
Azure. The Saga design pattern is a way to manage data consistency across microservices in distributed transaction scenarios. A saga is a sequence of transactions that updates each service and publishes a message or event to trigger the next transaction step.
A "saga" maintains process state. A more accurate term is a process manager. The term "saga" was popularised by NServiceBus which is why many people nowadays refer to it as a "NServiceBus saga". A true saga is a database concept.
Anyway, since an event dispatcher has no interest in process state it is not a process manager. A service bus, as you noted, can also act as an event dispatcher, typically to other systems, although a service bus handles a whole lot more.
There are ways to deal with process state without making use of a saga, e.g.: routing slips and "choreography". Process managers are more of an "orchestration" mechanism.
Process managers can make your life a whole lot simpler so it does a bit more than an event dispatcher.
Essentially your subscriber(s) will interact with your process manager to effect any changes related to the process.
You may be thinking that this is a bit like workflow and you will be correct. However, a workflow engine is quite a heavy affair whereas a process manager should be a first class citizen in your DDD world :)
The following is just a quick, off the top of my head, and broad sample. Initially the data to create a member is stored as state in the process manager. Only once the e-mail address has been verified is the actual member created and stored with its valid e-mail address.
Then a welcome e-mail is sent, perhaps using a service bus. Once the response from the EMailService
endpoint is received that the mail has been successfullly sent does that handler instruct the process manager that the e-mail has been sent and then completes the process manager.
So there would be a MemberRegistrationProcessRepository
. Completing a process may result in it being archived or even deleted if it is really no longer required.
I have a suspicion that event sourcing will lend itself nicely to process managers but to keep the sample simple I have put together the following based on what I have previously implemented myself.
What I have also done previously is to keep track of the status changes and we had an SLA of 15 minutes per status. This was monitored and all process managers sitting on a status for more than 15 minutes would be reported to the core operational team to investigate.
In C# one could have something like this:
public class MemberRegistrationProcess
{
public Guid ProcessId { get; private set; }
public string Name { get; private set; }
public EMailAddress EMailAddress { get; private set; }
public string Status { get; private set; }
public static MemberRegistrationProcess Create(string name, EMailAddress eMailAddress)
{
return new MemberRegistrationProcess(Guid.NewGuid(), name, eMailAddress, "Started");
}
public MemberRegistrationProcess(Guid processId, string name, EMailAddress eMailAddress, string status)
{
ProcessId = processId;
Name = name;
EMailAddress = eMailAddress;
Status = status;
}
public void EMailAddressVerified(IMemberRepository memberRepository)
{
if (!Status.Equals("Started"))
{
throw new InvalidOperationException("Can only verify e-mail address if in 'started' state.");
}
memberRepository.Add(new Member(Name, EMailAddress));
Status = "EMailAddressVerififed";
}
public void WelcomeEMailSent()
{
if (!Status.Equals("EMailAddressVerififed"))
{
throw new InvalidOperationException("Can only set welcome e-mail sent if in 'EMailAddressVerififed' state.");
}
Status = "WelcomeEMailSent";
}
public void Complete(Member member)
{
if (!Status.Equals("WelcomeEMailSent"))
{
throw new InvalidOperationException("Can only complete in 'WelcomeEMailSent' state.");
}
member.Activate();
Status = "Complete";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With