Skip to content

Jafly. Common EDA Library

Common EDA Library

Publish and consume events through a common envelope and transport-independent APIs.

Start with EventPublisher and the listener annotations; add a transport adapter for broker or database delivery. Choose publisher and consumer names explicitly when several transports coexist.

Maven artifact Packaging Source project
org.fireflyframework:fireflyframework-eda jar modules/eda

Part of Event-driven integration.

In the onboarding service

Once a submitted application must notify another service, define an application.submitted event and select its transport. The first-service tutorial has no event publisher, so adding delivery is a separate change with its own failure behavior.

Add the component

Installation and version availability explains source builds and published releases.

<dependency>
    <groupId>org.fireflyframework</groupId>
    <artifactId>fireflyframework-eda</artifactId>
    <version>26.10.20-SNAPSHOT</version>
</dependency>

Quick start

The injection examples use Lombok @RequiredArgsConstructor. Add its provided dependency and annotation processor as described in Constructor injection.

This optional extension publishes an application ID after the onboarding service has accepted a submission. The Java class is an API fragment: register it in a Spring application with EDA enabled, inject it into application code, and subscribe to or compose its returned Mono. It does not save the application or make the save and publication atomic.

import lombok.RequiredArgsConstructor;
import java.util.UUID;
import org.fireflyframework.eda.annotation.PublisherType;
import org.fireflyframework.eda.publisher.EventPublisherFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

@Service
@RequiredArgsConstructor
public class ApplicationEventPublisher {
    private final EventPublisherFactory publishers;



    public Mono<Void> submitted(UUID applicationId) {
        return publishers.getPublisher(PublisherType.APPLICATION_EVENT)
                .publish(applicationId, "application-submitted");
    }
}

APPLICATION_EVENT selects process-local Spring events explicitly. It does not reach another service or survive process failure. A distributed integration selects and configures a broker provider instead; define the event schema, destination, duplicate handling, and recovery before connecting business effects. The publishing API also offers @PublishResult for Spring-managed methods and @EventListener for consumers. An EventEnvelope is a Java record; access its payload with payload().

Configuration and wiring

The firefly.eda namespace is bound by EdaProperties.java. Nested configuration is declared in the linked class. Defaults are the field initializers.

Setting Java type Source default
firefly.eda.enabled boolean true
firefly.eda.default-publisher-type PublisherType PublisherType.AUTO
firefly.eda.default-connection-id String "default"
firefly.eda.default-destination String "events"
firefly.eda.default-serialization-format String "json"
firefly.eda.default-timeout Duration Duration.ofSeconds(30)
firefly.eda.metrics-enabled boolean true
firefly.eda.health-enabled boolean true
firefly.eda.tracing-enabled boolean true
firefly.eda.resilience Resilience new Resilience()
firefly.eda.publishers Publishers new Publishers()
firefly.eda.consumer Consumer new Consumer()

Property-based wiring conditions found in this module:

Public API and extension points

Type Contract and responsibility
EventPublisher Unified interface for publishing events to different messaging systems.
EventEnvelope Unified event envelope for both publishing and consuming events.
EventPublisherFactory Factory for creating and managing event publishers.
EdaProperties Configuration properties for the Event-Driven Architecture library.
EventConsumer Interface for consuming events from messaging systems.
Related component Why it belongs nearby
Kernel Share a consistent exception vocabulary across application, domain, and infrastructure boundaries.
Observability Provide a common metrics, tracing, health, and context-propagation foundation for framework components.

Build and test

Use Java 25. From the repository root, build this component with its required dependencies:

git clone https://github.com/fireflyframework/fireflyframework-jafly.git
cd fireflyframework-jafly
./mvnw -pl :fireflyframework-eda -am verify
./mvnw -pl :fireflyframework-eda -am install

verify runs the selected projects' tests. install makes the current build available in your local Maven repository.

Representative test sources:

Some tests use Testcontainers. A working Docker-compatible container runtime is required for those fixtures; inspect their annotations and the build output to distinguish executed tests from environment-dependent skips.

Test fixture

The shouldSupportMultipleDestinations test demonstrates one concrete behavior. Excerpt from EventListenerAnnotationTest.java. The test class supplies its fixtures and imports.

@Test
@DisplayName("Should support multiple destinations")
void shouldSupportMultipleDestinations() throws NoSuchMethodException {
    // Arrange
    Method method = TestListener.class.getMethod("listenerWithMultipleDestinations", Object.class);

    // Act
    EventListener annotation = method.getAnnotation(EventListener.class);

    // Assert
    assertThat(annotation).isNotNull();
    assertThat(annotation.destinations()).hasSize(3);
    assertThat(annotation.destinations()).containsExactly(
            "topic1",
            "topic2",
            "topic3"
    );
}

Detailed reference

Configuration

All properties live under the firefly.eda.* namespace (see EdaProperties). The EDA core is enabled by default; publishers and consumers are disabled by default and opt in per transport. A representative configuration:

firefly:
  eda:
    enabled: true                          # master switch for the whole module (default: true)
    default-publisher-type: AUTO           # AUTO | APPLICATION_EVENT | KAFKA | RABBITMQ | POSTGRES | NOOP
    default-connection-id: default         # connection key used when none is given
    default-destination: events            # destination used when none is specified
    default-serialization-format: json     # json | avro | protobuf
    default-timeout: 30s
    metrics-enabled: true
    health-enabled: true
    tracing-enabled: true

    resilience:
      enabled: true
      circuit-breaker:
        enabled: true
        failure-rate-threshold: 50
        minimum-number-of-calls: 10
        sliding-window-size: 10
        wait-duration-in-open-state: 60s
        permitted-number-of-calls-in-half-open-state: 3
      retry:
        enabled: true
        max-attempts: 3
        wait-duration: 500ms
        exponential-backoff-multiplier: 2.0
      rate-limiter:
        enabled: false
        limit-for-period: 100
        limit-refresh-period: 1s
        timeout-duration: 5s

    publishers:
      enabled: true                        # global publisher switch (default: false)
      application-event:
        enabled: true                      # in-JVM transport, always available in the core
        default-destination: application-events
      # --- requires the fireflyframework-eda-kafka adapter ---
      kafka:
        default:
          enabled: true
          bootstrap-servers: localhost:9092
          default-topic: events
      # --- requires the fireflyframework-eda-rabbitmq adapter ---
      rabbitmq:
        default:
          enabled: true
          host: localhost
          port: 5672
          username: guest
          password: guest
          default-exchange: events
          default-routing-key: event
      # --- requires the fireflyframework-eda-postgres adapter ---
      postgres:
        default:
          enabled: true
          host: localhost
          port: 5432
          database: app
          username: app
          password: secret
          schema: public
          outbox-table: firefly_eda_outbox
          auto-create-schema: true         # provision outbox table + NOTIFY trigger at startup

    consumer:
      enabled: true                        # global consumer switch (default: false)
      group-id: my-service
      concurrency: 1
      retry:
        enabled: true
        max-attempts: 3
        initial-delay: 1s
        max-delay: 5m
        multiplier: 2.0
      application-event:
        enabled: true
      kafka:
        default:
          enabled: true
          bootstrap-servers: localhost:9092
          topics: events
          auto-offset-reset: earliest
      rabbitmq:
        default:
          enabled: true
          host: localhost
          port: 5672
          queues: events-queue
          prefetch-count: 10
      postgres:
        default:
          enabled: true
          host: localhost
          port: 5432
          database: app
          username: app
          password: secret
          channels: events                 # destinations to LISTEN on
          polling-interval: 30s            # NOTIFY-loss fallback poll cadence (0s disables polling)
          max-attempts: 3                  # outbox row -> DEAD_LETTER after N failures
          batch-size: 50

Key properties

Property Default Description
firefly.eda.enabled true Master switch for the entire EDA module.
firefly.eda.default-publisher-type AUTO Transport used when none is specified. AUTO resolves KAFKA → RABBITMQ → POSTGRES → APPLICATION_EVENT.
firefly.eda.default-serialization-format json Serializer used by default (json, avro, protobuf).
firefly.eda.default-timeout 30s Default timeout for publish operations.
firefly.eda.metrics-enabled / health-enabled / tracing-enabled true Toggle Micrometer metrics, Actuator health, and tracing integration.
firefly.eda.publishers.enabled false Global opt-in for all publishers.
firefly.eda.consumer.enabled false Global opt-in for all consumers.
firefly.eda.consumer.group-id firefly-eda Default consumer group id (used by group-based transports such as Kafka).
firefly.eda.resilience.* see above Circuit breaker, retry and rate-limiter settings applied by ResilientEventPublisher.

The kafka.*, rabbitmq.* and postgres.* sub-trees are mapped by connection id (the default key shown above), letting you define multiple named connections per transport. These keys are recognized by the core's property schema even before the corresponding adapter is installed; the actual transport beans only activate when the matching adapter module is on the classpath and enabled.

Continue the application

Use the EDA - Kafka guide for the next part of this integration. The runnable submission/query service is in Getting started.

Source and further reading