Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Reveno - High Performance and Low Latency CQRS Framework

DZone's Guide to

Reveno - High Performance and Low Latency CQRS Framework

Reveno is a high performance, low latency CQRS framework. Here's an overview of Reveno, with an intro to what it is, and examples of defining a transactional model.

· Performance Zone
Free Resource

Transform incident management with machine learning and analytics to help you maintain optimal performance and availability while keeping pace with the growing demands of digital business with this eBook, brought to you in partnership with BMC.

Introduction

Most of the today's transaction processing solutions are suffering from an excessively complex architecture and hard maintainable infrastructure, not to mention an overall maintenance cost of them.

The purpose of the Reveno is to give an easy domain-oriented development tool with simple and transparent infrastructure, with perfectly fitted components for max performance. But easy doesn't mean simplistic. Instead, it's different in intention to give you as many options as possible, so you can choose the one that suits you best. More than that, it's still high performance and low latency tool; able to process millions of transaction per second with the microseconds latency.

This is a quick start guide, in which we will create an artificial banking system and cover everything from defining command objects to executing transactions and checking the results.

Please note, that in the given example we are not going to dive into much details and descriptions about particular parts of the Reveno, like a persistence, snapshotting and etc. You can get a complete overview of all major features at Architectural overview page.

Defining Transactional Model

In the Reveno, there are two ways in which you can define your model – mutable or immutable. In the given example we prefer to show the default way – immutable one. First of all, we should define an Account entity, which represents a single bank client:

public static class Account {
    public final String name;
    public final long balance;

    public Account add(long amount) {
        return new Account(name, balance + amount);
    }

    public Account(String name, long initialBalance) {
        this.name = name;
        this.balance = initialBalance;
    }
}

Since the Reveno applies CQRS pattern, we need to define a view for the Account domain class, which will be used from the query model:

public static class AccountView {
    public final long id;
    public final String name;
    public final long balance;

    public AccountView(long id, String name, long balance) {
        this.id = id;
        this.name = name;
        this.balance = balance;
    }
}

Commands and Transaction Actions

In order to perform any transaction, you need to execute the command, which will, according to some internal business logic, dispatch special transaction actions, which will perform actual state mutations. But in the given example we could simplify things a bit. Reveno has special DSL syntax, which is very helpful for simple cases, especially when command handler is doing nothing but dispatching single transaction action. That exactly looks like our case. But if you have tough latency and throughput requirements, we still recommend you to use basic API. You can find many examples of it.

First, we will define a new engine instance with file system storage located at some temporal directory:

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

The next step is to define a view mapper from transactional to query model:

reveno.domain().viewMapper(Account.class, AccountView.class, (id,e,r) -> new AccountView(id, e.name, e.balance));

That’s it. Now let's create our new transaction handler, which adds new account to the system:

DynamicCommand createAccount = reveno.domain()
    .transaction("createAccount", 
                 (t, c) -> c.repo().store(t.id(), new Account(t.arg(), 0)))
    .uniqueIdFor(Account.class).command();

Let’s quickly describe what’s going on here. We have created a createAccount command object and it’s handler, which adds new Account to the repository, assigning it new ID which was auto generated for the Account class and is accessible from t.id().

We will also need to define some transaction which will debit/credit the balance of an account (for simpleness, debit will be performed with negative numbers):

DynamicCommand changeBalance = reveno.domain()
     .transaction("changeBalance", (t, c) - > 
                 c.repo().remap(t.longArg(), Account.class, (id, a) -> a.add(t.intArg("inc")))
                )
     .command();

changeBalance command requires two arguments to be passed – an account ID and a balance delta value. The first argument is accessed namelessly by t.longArg(), while the second one with the name it was passed by – t.intArg("inc"). Since our model is immutable, we must store the new result of Account.add() into the repository.

Transaction Execution

The first transaction which we will execute is a new account creation. But before that, we must start an engine:

reveno.startup();
long accountId = reveno.executeSync(createAccount, map("name", "John"));

And add 10,000$ to the account, for example:

reveno.executeSync(changeBalance, map("id", accountId, "inc", 10_000));

Now your transactional model has single Account entity with a balance set to 10,000$. After that, we can access query model and check that all transactions have made appropriate changes to it:

Assert.assertNotNull(reveno.query().find(AccountView.class, accountId));
Assert.assertEquals(reveno.query().find(AccountView.class, accountId).name, "John");
Assert.assertEquals(reveno.query().find(AccountView.class, accountId).balance, 10_000);

Restoring System State

In a typical transactional environment, the results of every successful action must be saved on some physical storage, with the maximum balance between durability and performance aspect, thus minimizing the losses in emergency situations and maximizing the availability and responsiveness of the system. We had tried to solve all possible cases and requirements by providing a rich set of configurable options.

Conclusion

The Reveno is a relatively young, but quickly growing framework with already stable releases presented. You can visit our official website to find out a rich documentation space, as well as plenty of examples, which we are trying to extend as often as possible. Don't hesitate to ask us about anything on our GitHub issues page

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Topics:
java ,cqrs ,low latency ,high performance ,frameworks ,high availability

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}