Forwarding and effects in Java

A command can only act on one Entity at a time—​the Entity that received the command. For certain use cases, you might want a different Entity to handle the command. Rather than sending an immediate reply to a command, the receiving Entity can forward the command to another Entity. In cases where you simply want other Entities to be aware that an Entity processed a command, you can emit an effect.

You handle forwarding or emit effects in the receiving Entity’s command handler. The forwarding or effect can target any Entity within the service—​whether it is an instance of the same or different Entity type.

Transactional limitations

It’s important to note that forwarded commands, and commands emitted as side effects, are non-atomic—​there is no guarantee that the transactions either all succeeded or none. If the service, or the data store, fails while a forwarded command is executing, the original command that triggered it will not be rolled back.

If partial updates will cause problems, you should not use forwarding and effects to update multiple Entities at once.

Forwarding control to another Entity

To forward a command, you send a forward message that includes the call to invoke, and the message to invoke it with. The command won’t be forwarded until any state actions requested by the command handler have successfully completed. It is the responsibility of the forwarded action to return a reply that matches the type of the original command handler. Forwards can be chained arbitrarily long.

You might want to forward control, for example, from an Entity that processes an event stream. Events are associated with a particular Entity ID. Entities that process an event stream from a remote topic or from another event sourced Entity might have reason to update an Entity that did not receive the events. In this case, the command handler should extract the original Entity ID, and then forward the command to the target Entity.

Emitting effects on another Entity

An Entity may also emit one or more effects. An 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 effect is therefore ignored. Effects are only performed after the successful completion of any state actions requested by the command handler.

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 in "blocking" mode, 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.

You might want to emit 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.

Language support

For detailed documentation on implementing forwarding and effects in your favourite language, follow the links below:

To forward a command or emit an effect, you need a reference to the service call that you want to invoke. On the entity’s context, you then invoke forward or effect. The following sections provide examples.

Looking up service call references

Look up a reference to the service call that you want to use with the ServiceCallFactorynew tab interface. This is accessible on any context object via the serviceCallFactory()new tab method as shown below. You might want to look up the service call reference in the originating entity’s constructor. That way, you only have to look it up once.

private final ServiceCallRef<Hotitems.Item> itemAddedToCartRef;

public ShoppingCartEntity(Context ctx) {
  itemAddedToCartRef =
      ctx.serviceCallFactory()
          .lookup(
              "example.shoppingcart.ShoppingCartService", "ItemAddedToCart", Hotitems.Item.class);
}

Forwarding a command

The CommandContext for each entity type implements ClientActionContext to allow forwarding a command by invoking ClientActionContext.forwardnew tab. For example, if the item being processed in the addItem command is a "hot" item, we can make the HotItems entity aware of that item by forwarding a command:

@CommandHandler
public void addItem(Shoppingcart.AddLineItem item, CommandContext ctx) {
  // ... Validate and emit event

  ctx.forward(
      itemAddedToCartRef.createCall(
          Hotitems.Item.newBuilder()
              .setProductId(item.getProductId())
              .setName(item.getName())
              .setQuantity(item.getQuantity())
              .build()));
}

Emitting an effect

The CommandContext for each entity type implements EffectContext to allow emitting an effect by invoking EffectContext.effectnew tab. For example, upon successful completion of the addItem command by ShoppingCartEntity, if we also want to emit an effect on the HotItems entity, we would invoke the effect service call as:

@CommandHandler
public Empty addItem(Shoppingcart.AddLineItem item, CommandContext ctx) {
  // ... Validate and emit event

  ctx.effect(
      itemAddedToCartRef.createCall(
          Hotitems.Item.newBuilder()
              .setProductId(item.getProductId())
              .setName(item.getName())
              .setQuantity(item.getQuantity())
              .build()));

  return Empty.getDefaultInstance();
}

Please note that, contrary to command forwarding, the result of the effect is ignored by the current command addItem.