Process Manager

Process Manager coordinates and routes messages between aggregates within a Bounded Context (BC). It is a central processing unit which maintains a state of a business process and determines the next processing step based on intermediate results.

Event and Command Handlers are invoked by the ProcessManagerRepository which manages instances of a Process Manager class.

“Process Manager” pattern was first defined and brought to the common vocabulary by Kyle Brown and Bobby Woolf under the guidance of Martin Fowler in the book “Enterprise Integration Patterns”.

For more information on Process Managers (and the important difference between Process Manager and Saga), please see:

How to Define a Process Manager

A Process Manager is defined as a Java class which encapsulates and manages its state which is defined as a protobuf message.
The main steps to define a Process Manager are:

  • Select a type of identifiers of this kind of process managers. If you decide to use a typed identifier (which is recommended), you need to define it as a protobuf message.
  • Define a Process Manager state structure as a protobuf message.
  • Generate Java code for the ID and state types.
  • Create a new Process Manager class derived from ProcessManager abstract base class, passing the ID and state type parameters:
public class RegistrationProcessManager extends ProcessManager<ProcessManagerId, RegistrationProcess> {
    // code of the process manager
}

Constructor

A Process Manager must have a public constructor initializing a Process Manager ID. It must be public as it serves as a public API for Spine (it is used by the ProcessManagerRepository).

public RegistrationProcessManager(ProcessManagerId id) {
    super(id);
}

Event Handlers

A Process Manager handles domain events. In Event Handler, it usually performs the following:

  • sends commands to Aggregates based on conditions like current process state;
  • changes the current process state;
  • throws a Business Failure if the process is in an inappropriate state, etc.
@Subscribe
public void on(PaymentCompleted event, EventContext context) throws IllegalProcessStateFailure {
    final RegistrationProcess.State processState = getState().getProcessState();
    if (processState == RESERVATION_CONFIRMED) {
        setProcessState(PAYMENT_COMPLETED);
        sendConfirmOrderCommand(event);
    } else {
        throw new IllegalProcessStateFailure(event);
    }
}

Command Handlers

Process Managers can also handle commands, for example, to stop or restart the process. Handling commands is more rare case than handling events. Here is an example of Command Handler:

@Assign
public CommandRouted handle(StopRegistrationProcess stopCmd, CommandContext context) {
    final RegistrationProcess.State state = getState().getProcessState();
    if (state != COMPLETED) {
        setIsCompleted(true);
        final RejectOrder rejectOrderCmd = newRejectOrderCommand();
        final CancelSeatReservation cancelReservationCmd = newCancelSeatReservationCommand();
        return newRouter().of(stopCmd, context)
                .add(rejectOrderCmd)
                .add(cancelReservationCmd)
                .route();
    } else {
        throw newIllegalProcessStateFailure();
    }
}

Use a Command Router to create and post command(s) in response to a command received by the Process Manager. The routed commands are created on behalf of the actor of the original command. That is, the actor and zoneOffset fields of created CommandContext instances will be the same as in the incoming command.

results matching ""

    No results matching ""