Evento
Evento 3 is available — exactly-once, cluster-ready, tested to break ↓

Start Building
Responsive|
Distributed
Systems

Evento is the Java framework
to build RECQ architectures — where the architecture is enforced, not hoped for.

System State Store
System State Store
Message Gateway  
Message Gateway  
  Message Gateway
  Message Gateway
Event Store
Event Store
read/write
read/write
Projector
Projector
DB
DB
consume
consume
write
write
Projection
Projection
read
read
consume
consume
Command Gateway     
Command Gateway     
Query Gateway
Query Gateway
req/resp?
req/resp?
req/resp
req/resp
req/resp?
req/resp?
req/resp
req/resp
req/resp
req/resp
Service
Service
req/resp
req/resp
req/resp?
req/resp?
Invoker
Invoker
req/resp?
req/resp?
req/resp
req/resp
Observer
Observer
consume
consume
req/resp?
req/resp?
req/resp
req/resp
DB'
DB'
DB''
DB''
Saga
Saga
Internet
Internet
req/resp
req/resp
Aggregate
Aggregate
write?
write?

Develop RECQ
Components in Java

  • Easy implementation of Invokers, Services, Aggregates, Projectors, Projections, Sagas and Observers in Java.
  • Use annotations to define the component type and Command, Query or Event handlers.
  • Implement interactions with the Message Gateway.
  • Evento Framework works great even with other Java frameworks like Spring, Micronaut, Quarkus.

Hover the annotations to flip through real code from the live demo →

@Invoker
public class TodoListInvoker extends InvokerWrapper {

    @InvocationHandler
    public String createTodoList(String name, String user) throws Exception {
        var identifier = "TDLS_" + UUID.randomUUID();
        getCommandGateway()
                .send(new TodoListCreateCommand(identifier, name),
                      toUserMetadata(user))
                .get(30, TimeUnit.SECONDS);
        return identifier;
    }

    @InvocationHandler
    public CompletableFuture<TodoListView> findTodoListByIdentifier(String id) {
        return getQueryGateway()
                .query(new TodoListViewFindByIdentifierQuery(id))
                .thenApply(Single::getData);
    }
}
@Saga(version = 1)
public class ErpTodoListActivityRegistrationSaga {

    @SagaEventHandler(init = true, associationProperty = "identifier")
    public SagaState on(TodoListCreatedEvent event) { /* start tracking */ }

    @SagaEventHandler(associationProperty = "identifier")
    public void on(TodoListTodoCheckedEvent event, SagaState state,
                   CommandGateway commandGateway) throws Exception {
        if (event.isAllChecked()) {
            commandGateway.send(new ErpUserActivityRegisterCommand(
                    "TodoList", event.getIdentifier(), state.getUsages()
            )).get();
        }
    }
}
@Projection
public class TodoListProjection {

    @QueryHandler
    public Single<TodoListView> handle(TodoListViewFindByIdentifierQuery q) {
        return Single.of(repository.findById(q.getIdentifier())
                .map(TodoList::toView).orElseThrow());
    }

    @QueryHandler
    public Multiple<TodoListListItemView> handle(TodoListListItemViewSearchQuery q) {
        return Multiple.of(repository.search("%" + q.getNameLike() + "%",
                PageRequest.of(q.getPage(), q.getSize()))
                .map(TodoList::toListItemView).toList());
    }
}
@Projector(version = 2)
public class TodoListProjector {

    @EventHandler
    public void on(TodoListTodoCheckedEvent event, Metadata metadata,
                   Instant timestamp, ProjectorStatus status) {
        var list = repository.findById(event.getIdentifier()).orElseThrow();
        var todo = list.todo(event.getTodoIdentifier());
        todo.setCompletedAt(timestamp.atZone(ZoneId.systemDefault()));
        todo.setCompletedBy(metadata.get("user"));
        repository.save(list);
        if (status.isHeadReached()) sse.notifyListChanged(list.getId());
    }
}
@Aggregate
public class TodoListAggregate {

    @AggregateCommandHandler
    public TodoListTodoAddedEvent handle(TodoListAddTodoCommand command,
                                         TodoListAggregateState state) {
        Assert.isTrue(state.getTodos().size() < 5,
                "Error: Todo list is full");
        return new TodoListTodoAddedEvent(command.getIdentifier(),
                command.getTodoIdentifier(), command.getContent());
    }

    @EventSourcingHandler
    public void on(TodoListTodoAddedEvent event,
                   TodoListAggregateState state) {
        state.getTodos().put(event.getTodoIdentifier(), false);
    }
}

Evento GUI · Catalog

Navigate the Catalog

The rigorous semantic of the RECQ Pattern lets Evento Server generate the System Catalog automatically from your code — explore and visualize the interactions of every Payload, Component and Bundle.

// Your javadoc, your annotations, your handlers…

/**

 * Manager of the user's todo lists:

 * validates commands, emits events.

 */

@Aggregate

public class TodoListAggregate { … }

scanned at startup — zero extra work

📖 TodoListAggregate Aggregate
Manager of the user's todo lists: validates commands, emits events.
↗ view source on GitHub

Evento GUI · Documentation

Documentation that can't lie

  • Evento Server scans your bundles at startup — payloads, components and handlers become the catalog. No wiki, no stale diagrams.
  • Rename a command, add a handler — the docs update themselves with the next deploy.
  • Every handler deep-links to its source line on GitHub.

Evento GUI · Flows & Application Graph

Understand your RECQ Architecture

Flows render every interaction as a tree — how components cooperate, where to optimize. Explore your entire system or a subset, starting from a Payload, a Component or a Bundle, then switch to the Application-Graph to interact with the whole architecture at once.

Evento GUI · Performance

What-If Performance Analysis

Given a workload, estimate the concurrent requests handled by each handler, component and bundle — the algorithm detects the bottleneck of every interaction. Tune workload, service time and invocation frequency to find the best optimization or estimate computational resources before you buy them.

Available now

Evento 3

Everything you need to build a RECQ system in Java and run it from one site to three continents — every feature public, the client libraries under Apache-2.0.

Seven RECQ components

Aggregates, Services, Projectors, Projections, Sagas, Observers and Invokers — annotated Java classes that announce themselves to the server. No deployment descriptors.

Exactly-once commands

Idempotency keys make every retry safe on whichever server it lands; sagas, projectors and observers derive theirs automatically.

Aggregates that never fork

Every event carries its aggregate version under a unique index: two commands deciding from the same state can never both be stored.

Cluster from one site to three continents

Several servers on one PostgreSQL event store. Bundles fail over, fail back on their own, and are answered close to the caller.

Highly available consumers

Run every consumer, per context, in as many replicas as you like: one holds the lane, another takes over when it goes — no event lost or repeated.

Consumers that wait, not poll

The server tells each bundle when events it handles arrive. Updates flow at once, and an idle system costs the event store almost nothing.

Queries at any size

Streamed and paged answers with flow control, bundle-to-bundle streams, and read-your-writes when a query must see a command.

Payloads that evolve

Rename or reshape a command, event or query: upcasters read the history written with the old class, the event store is never rewritten.

Secure by default

Bus token, TLS, payload allowlist and real passwords required in production; single sign-on and audit trail for the console.

Privacy built in

Personal data encrypted per data subject: delete the subject's key and it is erased from the whole event history.

Observable

A console generated from your code, OpenTelemetry tracing and Prometheus metrics.

Any Java stack

Plain Java, Spring, Micronaut or Quarkus, a Spring Boot starter, test fixtures and an in-process broker, on Java 25.

Tested to break

Proven under failure, not only in the happy path

Exactly-once, failover and consumers that never skip an event are promises you should not take on trust. Evento 3 is checked against them on a three-continent lab, through rolling upgrades, and on the test suites of two production systems — a marketplace and a manufacturing-execution system — and benchmarked for what each command and query costs.

1,000+

automated tests on every change — PostgreSQL 16 and 17, MySQL 8.4, real TCP between bundles and servers

19

chaos scenarios on a three-continent lab: servers killed and frozen, a region cut off, database failovers, clock skew

0

acknowledged commands lost or executed twice, and no aggregate forked, in any scenario

3 × 3

contexts × replicas, the lane holder killed mid-stream: every event applied once and in order

3,900

commands a second stored by one server on a 4-core machine, p99 about 60 ms

7 ms

median round trip of a command on one machine, timed phase by phase from arrival to reply

Born from research, proven in production

7

component types — formally minimal, proven irredundant

235 kLOC

production manufacturing-execution system running on RECQ — 206 components, censused

2023

first peer-reviewed publication (QualITA’23) and founding master’s thesis

Read the research →

Don’t take our word for it — use it live

A real RECQ application and its Evento Server run in the open: play with the todo demo, then open the GUI and watch your own commands flow through the system.

The live todo demo — a populated list with checked todos, running on demo.eventoframework.com

Do you find Evento Framework and RECQ Architectures interesting?

Evento Framework is FREE and Open Source. You can always support the project by giving a star on GitHub or contributing to the code base.