Skip to content
Mario edited this page Sep 23, 2026 · 1 revision

Event-driven messaging

Vyra provides a clean publish/subscribe model for one-way, fire-and-forget notifications. Events allow services to broadcast state changes to any number of interested subscribers without coupling producers to consumers.


Messaging patterns compared

Feature Requests (request) Broadcast requests (broadcastRequest) Events (publish)
Pattern Point-to-point (1-to-1) Parallel query (1-to-N, first wins) Broadcast notification (1-to-many)
Delivery model Round-robin worker queue Broadcast to workers; silent non-owners Delivered to all active subscribers
Return type CompletionStage<T> CompletionStage<T> CompletionStage<Void> (network ack)
Delivery guarantee Best-effort with timeout Best-effort with timeout Fire and forget
Best for CRUD queries, transactions Locating partitioned entities/sessions State-change notifications, telemetry

How events work

flowchart TD
    Publisher["Order Service<br/>(publisher)"]:::pub
    Topic[("evt:order-events<br/>(channel)")]:::topic
    Sub1["Inventory Service<br/>(subscriber)"]:::sub
    Sub2["Notification Service<br/>(subscriber)"]:::sub
    Sub3["Analytics Service<br/>(subscriber)"]:::sub

    Publisher -->|publish| Topic
    Topic -->|broadcast| Sub1
    Topic -->|broadcast| Sub2
    Topic -->|broadcast| Sub3

    classDef pub fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
    classDef topic fill:#fff3e0,stroke:#e65100,color:#bf360c
    classDef sub fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
Loading

One publisher, many subscribers: the event is delivered to every active subscriber, at most once.

When an event is published:

  1. Serialization: the publisher creates a Message envelope (kind = EVENT, destination = "order-events"), serializes it via your configured serializer, and hands the bytes to the transport.
  2. Network delivery: the transport publishes the raw bytes to the channel topic (evt:order-events).
  3. Offload & dispatch: each subscriber node receives the bytes, deserializes the envelope on its handler executor (never blocking the network thread), and executes your callback.

Code examples

1. Define and register event records

// Immutable event wire contract
public record OrderPlacedEvent(String orderId, String customerId, double totalAmount) {}

// Register with a stable message identifier on both publisher and subscribers
vyra.register("order.placed", OrderPlacedEvent.class);

2. Subscribe to an event channel

Use subscribe(channel, eventType, consumer). Subscriptions immediately start listening, and callbacks run on the handler executor:

vyra.subscribe("order-events", OrderPlacedEvent.class, event -> {
    System.out.println("Processing inventory update for order: " + event.orderId());
});

Note

You can register multiple subscribers for different event types on the same channel or subscribe multiple services to the same channel. Every active subscriber receives a copy of each matching event.

3. Publish an event

Use publish(channel, event). It returns a CompletionStage<Void> that completes once the transport has accepted the message:

OrderPlacedEvent event = new OrderPlacedEvent("ord-99", "cust-12", 249.95);

vyra.publish("order-events", event)
        .thenRun(() -> System.out.println("Event successfully dispatched to transport"))
        .exceptionally(err -> {
            System.err.println("Transport failed to send event: " + err.getMessage());
            return null;
        });

Delivery semantics: at-most-once

Vyra’s event system is designed for at-most-once delivery:

  • No persistence: if no subscribers are listening when an event is published (e.g., a service is restarting), the event is not buffered or queued.
  • No redelivery: if a subscriber throws an exception while handling an event, Vyra logs the error, but does not retry delivery.
  • Why? events are meant for broadcasting ephemeral state changes, not for transactional workloads. If you need guaranteed delivery or a response, use Request / response.

Designing with at-most-once events

  • Recommended use cases:
    • Cache invalidation (e.g. "Customer 42's profile was updated, evict local cache").
    • Real-time UI push notifications and websocket broadcasts.
    • Telemetry, metrics, and heartbeat reporting.
  • Anti-patterns:
    • Financial ledger transactions where losing an event causes corrupted state.
    • Critical tasks requiring guaranteed delivery confirmation (use Request / response).

Next steps

  • Compare event delivery across transports in Transports.
  • Understand the 3-layer threading model in Concurrency.
  • Review the complete Best practices for production deployments.

Vyra documentation

Getting started and messaging patterns

Transports and formats

Inside the framework

Reference

Clone this wiki locally