Reactive Domain

Process Managers

Within systems, we have times where we need to orchestrate an action over many steps that may be within a single request, or over multiple requests. For these instances, Process Managers come into play.

With my learning and exploration of Event-Sourced applications, I was looking for a thing called a process manager. Even within reactive-domain, there is an abstract class that defines a Process Manager. Ok, step 1, let’s inherit this class and go forward. NOT!

A Process Manager isn’t a single defined item, but a theory or methodology to solve an issue. Anything that needs to keep state over the process can, and should, inherit the Process Manager class, but beyond that, they can take various forms.

The easiest to see is going to be a stateful process manager, which may look something like this:

public static class TheProcessMsgs {
    public class Initiated : Event {
        public readonly Guid TheProcessId;
        public readonly string Title;
        public readonly string ASupportingArtifact;

        public Initiated(Guid theProcessId, string title, string aSupportingArtifact) {
            TheProcessId = theProcessId;
            Title = title;
            ASupportingArtifact = aSupportingArtifact;
        }
    }

    public class NextStepRaised : Event {
        public readonly Guid TheProcessId;

        public NextStepRaised(Guid theProcessId) {
            TheProcessId = theProcessId;
        }
    }
    
    public class OtherAggregateMsgReceived : Event {
        public readonly Guid TheProcessId;
        public readonly Guid OtherAggregateId;

        public OtherAggregateMsgReceived(Guid theProcessId, Guid otherAggregateId) {
            TheProcessId = theProcessId;
            OtherAggregateId = otherAggregateId;
        }
    }
}

public class TheProcess : ProcessManager {
    public TheProcess(Guid theProcessId, string title, string aSupportingArtifact, ICorrelatedMessage msg) : base(msg) {
        Ensure.NotEmptyGuid(theProcessId, nameof(theProcessId));
        Ensure.NotNullOrEmpty(title, nameof(title));
        Ensure.NotNullOrEmpty(aSupportingArtifact, nameof(aSupportingArtifact));

        RegisterEvents();

        Raise(new TheProcessMsgs.Initiated(theProcessId, title, aSupportingArtifact));
    }

    public TheProcess() : base() {
        RegisterEvents();
    }

    private void RegisterEvents() {
        Register<TheProcessMsgs.Initiated>(Apply);
        Register<TheProcessMsgs.OtherAggregateMsgsReceived>(Apply);
    }

    public void Handle(IMessage msg) {
        switch (msg) {
            case SomeOtherAggregateMsgs.SomeEvent otherEvent:
                Raise(new TheProcessMsgs.OtherAggregateMsgReceived(Id, otherEvent.SomeOtherAggregateId))
                break;
        }
    }

    public void NextStep() {
        Raise(TheProcessMsgs.NextStepRaised(Id));
    }

    private void Apply(TheProcessMsgs.Initiated msg) { 
        Id = msg.TheProcessId;
    }

    private void Apply(TheProcessMsgs.OtherAggregateMsgReceived msg) {
        // update state from msg.
    }
}

With this style process manager, you can receive outside messages, notate the message through this process’ stack of events, and subsequently move forward on additional steps.

Other forms of process managers may not have state, and may just subscribe to events from the Dispatcher:

public class ProcessHandlers : IHandle<SomeOtherAggregateMsgs.SomeEvent> {
    private readonly ISubscriber _subscriber;
    private readonly IPublisher _publisher;

    public ProcessHandlers(ISubscriber subscriber, IPublisher publisher) {
        _subscriber = subscriber;
        _publisher = publisher;

        _subscriber.Subscribe<SomeOtherAggregateMsgs.SomeEvent>(this);
    }

    public void Handle(SomeOtherAggregateMsgs.SomeEvent msg) {
        //... handle the event.
        //... [possibly] Raise new Commands
    }
}

The point being, there is no absolute “Here is a process manager”, and more of a “Here’s a method to react to a series of events from within the system. There is tons more to discuss, but for now, this is my take on it.