Skip to content

Commands

Artem Dmitriev edited this page Jun 26, 2017 · 1 revision

Introduction

Commands are the core part of the Reveno engine. They are responsible for initial request handling, business/validation logic, and finally dispatching transaction actions, which are an actual transactional model state mutators. Every command object should have it’s own handler registered in the system. It can return some result, which will be eventually available after the whole transaction execution. Typically, the result can be an identificator of the new entity instance or anything else. When Reveno DSL is used, command and transaction action are compound into the same handler, which makes the code more clear in simple cases.

Command batches

When you execute a command, all further transaction action(s) execution is guaranteed to be an atomic - either everything is executed completely or nothing at all. Technically, it’s achieved by using special repository implementation, which supports rollback operation. Sometimes you might need to group commands together into the same transaction scope. This is achieved by calling reveno.performCommands(commandsArray), which takes multiple command objects as an argument.

Command declaration

There are no restrictions about how command class should be implemented. The only requirement is that it should be serializable by at least one serializer in provided serializers chain Here is an example of typical сommand declaration:

public class AddToBalanceCommand {
	public final long accountId;
	public final long amount;
	public final Currency currency;

	public AddToBalanceCommand(long accountId, long amount, Currency currency) {
		this.accountId = accountId;
		this.amount = amount;
		this.currency = currency;
	}
}

This command contains all required fields for balance incrementation of the account. To make it work, command handler for AddToBalanceCommand class should be defined, and appropriate transaction action(s) dispatched from it.

Command handler

When some command object is passed to an engine, appropriate handler is being executed. There are two possible types of such handlers - result or resultless. Let’s declare command handler for previous AddToBalanceCommand command class. First of all, we need to obtain a started engine instance:

Reveno reveno = new Engine("/tmp/reveno-engine");

Then, we have to declare the handler itself, extending our previous command class (but this isn’t a rule - you can define it somewhere else, or even pass as a lambda expression):

public static class AddToBalanceCommand {
	public final long accountId;
	public final long amount;
	public final Currency currency;

	public static void handleCommand(AddToBalanceCommand cmd, CommandContext ctx) {
		// some business logic here
	}

	public AddToBalanceCommand(long accountId, long amount, Currency currency) {
		this.accountId = accountId;
		this.amount = amount;
		this.currency = currency;
	}
}

And then register it appropriately:

reveno.domain().command(AddToBalanceCommand.class, AddToBalanceCommand::handleCommand);

When you will start an engine and execute AddToBalanceCommand, AddToBalanceCommand::handleCommand handler will be called, containing your command object in a cmd, and special CommandContext in a ctx argument.

CommandContext

CommandContext is passed along with every command handler, and contains ancillary methods. The most important are:

  • Repository repository() - returns a read-only instance of a transactional model repository. It’s used mainly for business and validation logic. When a batch of commands is being executed, changes of all previous transaction actions are visible to subsequent ones.
  • long id(Class entityType) - returns next unique ID for an entity class. Since the Reveno restricts to only use long identifiers, it’s mostly recommended and safe way of getting unique ones.
  • CommandContext executeTxAction(Object txAction) - dispatches transaction action, that will be executed as a part of that command. Strong FIFO ordering is guaranteed here.

In conclusion, it worth to mention again, that commands are used for some validation, aggregation, ID generation logic, which results will be passed to a subsequent transaction action(s) dispatched from it.

Examples

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

Clone this wiki locally