DDD, difference between a Saga and an Event Dispatcher?

fj123x picture fj123x · Sep 14, 2015 · Viewed 10.2k times · Source

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?

Answer

Eben Roux picture Eben Roux · Sep 15, 2015

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 :)

Process Manager Example

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";
    }
}