Running Side Effects in Java

Emitting effects on another component

An Entity or an Action may also emit one or more side effects. A side effect is something whose result has no impact on the result of the current command—​if it fails, the current command still succeeds. The result of the side effect is therefore ignored. When used from inside an Entity, side effects are only performed after the successful completion of any state actions requested by the command handler.

There is no guarantee that a side effect will be executed successfully. If a failure occurs after the command is fully handled, effects might not be executed. Side effects are not retried in case of failures.

Side effects may be declared as synchronous or asynchronous. Asynchronous commands run in a "fire and forget" fashion. The code flow of the caller (the command handler of the entity which emitted the asynchronous command) continues while the command is being asynchronously processed. Meanwhile, synchronous commands run sequentially, that is, the commands are processed in order, one at a time. The final result of the command handler, either a reply or a forward, is not sent until all synchronous commands are completed.

Use case: mobile notification

You might want to run side effects to notify interested parties of a change in state. For example, after a withdrawal is made from a bank account, an account entity could send a notification to the account owner’s mobile phone.

Emitting a side effect

To illustrate how you can emit a side effect, we can build on top of the Action as a Controller example. In that previous example, we build a controller around the Value Entity Counter and forwarded the incoming request after modifying it.

This time, instead of using a forward, we will call the entity using a side effect.

Implementing the Action

The class DoubleCounterAction gets generated for us based on the same proto file defined in Action as a Controller.

src/main/java/com/example/actions/DoubleCounterAction.java
/**
 * An action.
 */
public class DoubleCounterAction extends AbstractDoubleCounterAction {

  private final ServiceCallRef<CounterApi.IncreaseValue> increaseCallRef;

  public DoubleCounterAction(ActionCreationContext creationContext) {
    this.increaseCallRef =
        creationContext.serviceCallFactory() (1)
            .lookup(
                "com.example.CounterService",
                "Increase",
                CounterApi.IncreaseValue.class);
  }

  /**
   * Handler for "Increase".
   */
  @Override
  public Effect<Empty> increase(CounterApi.IncreaseValue increaseValue) {
    int doubled = increaseValue.getValue() * 2;
    CounterApi.IncreaseValue increaseValueDoubled =
        increaseValue.toBuilder().setValue(doubled).build(); (2)

    return effects()
            .reply(Empty.getDefaultInstance()) (3)
            .addSideEffect( (4)
                SideEffect.of(increaseCallRef.createCall(increaseValueDoubled)));
  }

}
1 In the constructor, we use the ActionCreationContext to create a ServiceCallRef pointing to the Increase method in the Counter entity.
2 On incoming requests, we double the value of IncreaseValue
3 We build a reply using Empty.getDefaultInstance().
4 and we attach a side effect to it. The side effect is the call to the Counter entity.

Please note that, the result of a side effect is ignored by the current command meaning that even if the call to the Counter entity fails, the Action reply will succeed.