Transaction Actions

In order to perform any transaction in Reveno the first thing you must do is execute a command. Typically, commands are used to perform some aggregation and validation logic, using read-only access to the repository. But in order to perform any side effect to the transactional model, special “transaction action(s)” must be dispatched. All transaction actions dispatched from any command are united in a single atomic transaction.

If you are starting from scratch, we recommend you to follow Architectural Overview page first.

Like a command object, transaction action can be a simple POJO,which instance will be passed to the registered handler at runtime. The only restriction is that it must be serializable by the current serializers chain. After execution, transaction actions are persisted to append-only storage. When an engine restarts, it replays every action one by one. Reveno also supports snapshots for a quicker replays..

It’s also notable that you don’t need to introduce any kinds of locks into your transaction action or command code. Also, if your command handler is doing nothing but dispatches single transaction action, or some simple validation logic, we propose you to use our DSL syntax, as it’s more straightforward and readable. See our Quickstart Guide.

Every transaction action should have it’s own handler registered in the system.. In order to show how, let’s imagine that we have some AddToCart transaction action, and we need to define handler which will be doing particular who will perform the addition of certain goods in the cart:

public static void handler(AddToCart action, TransactionContext ctx) {
    // TODO business logic here
}

And then just register it in the system:

Reveno reveno = new Engine(dir);
reveno.domain().transactionAction(AddToCart.class, this::handler);

Or even use Java 8 lambda here:

Reveno reveno = new Engine(dir);
reveno.domain().transactionAction(AddToCart.class, (a, ctx) -> {
	// TODO business logic here
});

As you can see, handler accepts two parameters here. The first one is a transaction action instance itself, while the second one is a TransactionContext of the current operation, which has next methods:

  • EventBus eventBus() – the Event Bus, which is used to publish async events from handlers. Read more about Reveno event system on a dedicated page.
  • WriteableRepository repository() – transactional model repository instance with read-write access.
  • Map<Object, Object> data() – ancillary data holder between different transaction actions in the currently executing command scope. It’s worth noting that its frequent usage may indicate a poor design.

Occasionally any transaction action might fail. In that case, all already made changes should be discarded. Depending on the command context, in which it’s executed, this might also mean that changes of previous transaction actions should be rolled back as well.

In the default case all rollback job is done automatically by the underlying repository. Its implementation might be different for different types of objects, which is described in details on the dedicated page.

The only exception comes for mutable objects repository. At some point you might find that such repository performance is not acceptable for you. In that case, you should define your own compensating actions on every transaction action.

Check out our examples to practically get everything, that was covered (or possibly not) in the given topic.