Evento
Evento 2 is out — new transport, exactly-once QoS ↓

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.

New

Evento 2 is here

The second generation of the framework rebuilds the entire transport and consumer runtime — same seven components, industrial-grade internals.

Binary CBOR protocol

A new Netty transport with length-prefixed CBOR frames, transparent chunking (no message-size limit) and optional TLS.

Exactly-once QoS

Broker and bundle dedupe caches with response replay across reconnects — commands and events arrive once, in order.

Self-registering bundles

No JAR uploads, no deploy scripts: bundles announce themselves over the bus and appear in the catalog instantly.

Java 25 + virtual threads

Built for modern Java: virtual-thread executors end-to-end and Spring Boot 3.5 LTS compatibility.

Pluggable consumer stores

Projector, saga and observer state on Postgres or MySQL via focused SPIs — auto-migrated at startup.

Source-linked GUI

Every handler in the GUI deep-links to its exact source line on GitHub. The catalog is the codebase.

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.