Skip to content

Commit ca7eb37

Browse files
Andrés Contreras GuillénAndrés Contreras Guillén
authored andcommitted
docs: comprehensive, polished README
1 parent 1fda77f commit ca7eb37

1 file changed

Lines changed: 170 additions & 59 deletions

File tree

README.md

Lines changed: 170 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Java](https://img.shields.io/badge/Java-21%2B-orange.svg)](https://openjdk.org)
66
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.x-green.svg)](https://spring.io/projects/spring-boot)
77

8-
> Unified event-driven architecture library with Kafka, RabbitMQ, PostgreSQL (LISTEN/NOTIFY + outbox), and Spring Application Events support.
8+
> Reactive, transport-agnostic event-driven architecture core for Spring Boot — one publisher/consumer API with annotation-driven publishing, declarative listeners, and pluggable Kafka, RabbitMQ and PostgreSQL adapters.
99
1010
---
1111

@@ -23,92 +23,191 @@
2323

2424
## Overview
2525

26-
Firefly Framework EDA provides a standardized messaging abstraction for event-driven architectures, supporting multiple broker implementations through a unified publisher/consumer API. It enables reactive event publishing and consumption with built-in support for Apache Kafka, RabbitMQ, PostgreSQL (via `LISTEN`/`NOTIFY` backed by a transactional outbox table), and Spring Application Events as transport mechanisms.
26+
Firefly Framework EDA is the **core abstraction** for event-driven architectures across the Firefly Framework. It defines a single, reactive publisher/consumer SPI (`EventPublisher`, `EventConsumer`) and a rich annotation model (`@EventPublisher`, `@PublishResult`, `@EventListener`) so application code emits and handles domain events without binding to any particular broker. The same business code runs unchanged whether events flow over Apache Kafka, RabbitMQ, PostgreSQL, or the in-process Spring application event bus.
2727

28-
The library features annotation-driven event publishing (`@EventPublisher`, `@PublishResult`), declarative event listeners (`@EventListener`), and a comprehensive set of event filtering, serialization, and error handling capabilities. It includes support for JSON, Avro, and Protobuf message serialization formats.
28+
This module ships **everything that is broker-independent**: the SPI, the annotation aspects, the `EventPublisherFactory` (which auto-discovers every `EventPublisher` bean on the classpath and selects one by type or priority), the `EventEnvelope` metadata model, pluggable serialization (JSON, Avro, Protobuf), composable event filters, a resilient publisher wrapper (circuit breaker, retry, rate limiter via Resilience4j), a dead-letter-queue handler, plus Actuator health indicators and Micrometer metrics. Out of the box, the core provides a working **Spring Application Events** transport (in-JVM publish/consume) and a `Noop` transport for testing — no external infrastructure required.
2929

30-
The resilient publisher wrapper provides circuit breaker integration, while the dead letter queue handler ensures no events are lost during processing failures. Metrics collection and health indicators provide full observability into the messaging infrastructure.
30+
The concrete broker transports are now **separate adapter modules**. The core discovers them at runtime: add the adapter dependency, enable it in `firefly.eda.*` configuration, and the `EventPublisherFactory` picks it up automatically. The `firefly.eda.default-publisher-type` property (or `PublisherType.AUTO`) selects which transport a publish goes to, with `AUTO` resolving in priority order `KAFKA → RABBITMQ → POSTGRES → APPLICATION_EVENT` based on what is configured and on the classpath.
31+
32+
### Transport adapters
33+
34+
| Transport | Adapter module | Selected by | Persistence | Ordering |
35+
|-----------|----------------|-------------|-------------|----------|
36+
| Apache Kafka | [`fireflyframework-eda-kafka`](https://github.com/fireflyframework/fireflyframework-eda-kafka) | `PublisherType.KAFKA` | yes | yes |
37+
| RabbitMQ (AMQP) | [`fireflyframework-eda-rabbitmq`](https://github.com/fireflyframework/fireflyframework-eda-rabbitmq) | `PublisherType.RABBITMQ` | yes | no |
38+
| PostgreSQL (outbox + `LISTEN`/`NOTIFY`) | [`fireflyframework-eda-postgres`](https://github.com/fireflyframework/fireflyframework-eda-postgres) | `PublisherType.POSTGRES` | yes | yes |
39+
| Spring Application Events (in-JVM) | **built into this core** | `PublisherType.APPLICATION_EVENT` | no | no |
40+
| No-op (testing / disabled) | **built into this core** | `PublisherType.NOOP` | no | no |
41+
42+
The `firefly.eda.*` configuration schema (including `publishers.kafka.*`, `publishers.rabbitmq.*`, `publishers.postgres.*` and their consumer counterparts) is defined here in the core so it remains stable and consistent regardless of which adapters you install.
3143

3244
## Features
3345

34-
- Multi-broker support: Apache Kafka, RabbitMQ, PostgreSQL (`LISTEN`/`NOTIFY` + outbox), Spring Application Events
35-
- Annotation-driven publishing: `@EventPublisher`, `@PublishResult`
36-
- Declarative event listening: `@EventListener` with SpEL-based filtering
37-
- Event envelope pattern with metadata propagation
38-
- Pluggable serialization: JSON, Avro, Protobuf
39-
- Event filtering: type-based, header-based, destination-based, composite
40-
- Dead letter queue (DLQ) handling for failed messages
41-
- Resilient publisher with circuit breaker support
42-
- Dynamic event listener registration at runtime
43-
- AMQP admin auto-configuration for RabbitMQ exchanges and queues
44-
- Transactional outbox table for PostgreSQL with auto-created schema and trigger
45-
- Custom error handling strategies with metrics and notification handlers
46-
- Health indicators and metrics for Actuator integration
47-
- Spring Boot auto-configuration for Kafka, RabbitMQ, and PostgreSQL
46+
- **Unified reactive SPI**`EventPublisher` and `EventConsumer` expose `Mono`/`Flux` APIs; one programming model for all transports
47+
- **Annotation-driven publishing**`@EventPublisher` (publish a parameter or wrapped method args, `BEFORE`/`AFTER`/`BOTH` execution) and `@PublishResult` (publish the method's return value), both with SpEL `condition`, `key`, `headers`, `destination` and `eventType`
48+
- **Declarative listeners**`@EventListener` with `destinations`, `eventTypes` (glob patterns), SpEL `condition`, `priority`, per-listener retry and `errorStrategy`
49+
- **Pluggable transports** — Kafka, RabbitMQ and PostgreSQL adapters are independent modules; the in-JVM Spring Application Event transport and a `Noop` transport ship in the core
50+
- **Auto transport selection**`EventPublisherFactory` discovers all `EventPublisher` beans and resolves `PublisherType.AUTO` in priority order `KAFKA → RABBITMQ → POSTGRES → APPLICATION_EVENT`
51+
- **Dynamic destination selection**`getPublisherWithDestination(...)` overrides the default topic/queue/channel at runtime via `DestinationAwarePublisher`
52+
- **Event envelope pattern**`EventEnvelope` carries payload plus metadata (event type, destination, headers, timestamps) end to end
53+
- **Pluggable serialization**`MessageSerializer` with built-in JSON, Avro and Protobuf implementations (`SerializationFormat`)
54+
- **Composable filtering**`EventTypeFilter`, `HeaderEventFilter`, `DestinationEventFilter` and `CompositeEventFilter` implementing a common `EventFilter` SPI
55+
- **Resilience**`ResilientEventPublisher` wraps any publisher with Resilience4j circuit breaker, retry and rate limiter (all configurable, opt-in by classpath)
56+
- **Dead-letter handling**`DeadLetterQueueHandler` and `DeadLetterQueueEvent` capture messages that exhaust retries
57+
- **Custom error handling**`CustomErrorHandler` SPI with a registry and built-in `MetricsErrorHandler` / `NotificationErrorHandler`
58+
- **Dynamic registration**`DynamicEventListenerRegistry` registers and removes listeners at runtime
59+
- **Observability**`EdaHealthIndicator` for Actuator and `EdaMetrics` for Micrometer, wired through `fireflyframework-observability`
60+
- **Spring Boot auto-configuration**`FireflyEdaAutoConfiguration` activates on `firefly.eda.enabled=true` (default) and component-scans the whole module
4861

4962
## Requirements
5063

51-
- Java 21+
64+
- Java 21+ (Java 25 recommended)
5265
- Spring Boot 3.x
5366
- Maven 3.9+
54-
- Apache Kafka, RabbitMQ, or PostgreSQL 11+ (depending on chosen transport)
67+
- A transport adapter for production messaging: an Apache Kafka broker, a RabbitMQ broker, or a PostgreSQL 11+ database (the in-JVM Spring Application Event transport needs no external infrastructure)
5568

5669
## Installation
5770

71+
Add the EDA core. The version is managed by the Firefly BOM / parent, so you normally omit it:
72+
5873
```xml
5974
<dependency>
6075
<groupId>org.fireflyframework</groupId>
6176
<artifactId>fireflyframework-eda</artifactId>
62-
<version>26.02.07</version>
77+
<!-- version managed by fireflyframework-bom / fireflyframework-parent -->
78+
</dependency>
79+
```
80+
81+
To publish/consume over a real broker, add the matching transport adapter alongside the core. For example, for Apache Kafka:
82+
83+
```xml
84+
<dependency>
85+
<groupId>org.fireflyframework</groupId>
86+
<artifactId>fireflyframework-eda-kafka</artifactId>
87+
<!-- version managed by the BOM -->
6388
</dependency>
6489
```
6590

91+
Swap the artifact for `fireflyframework-eda-rabbitmq` or `fireflyframework-eda-postgres` as needed. You can install more than one adapter and route events per transport with `PublisherType`.
92+
6693
## Quick Start
6794

95+
With only the core on the classpath, events flow over the in-JVM Spring Application Event bus — perfect for a single instance, tests, or local development.
96+
6897
```java
6998
import org.fireflyframework.eda.annotation.EventPublisher;
7099
import org.fireflyframework.eda.annotation.EventListener;
71100
import org.fireflyframework.eda.event.EventEnvelope;
101+
import org.springframework.stereotype.Service;
102+
import reactor.core.publisher.Mono;
72103

73104
@Service
74105
public class OrderService {
75106

76-
@EventPublisher(topic = "orders")
107+
// Publishes the method's return value to the "orders" destination after it completes.
108+
@PublishResult(destination = "orders", eventType = "order.created")
77109
public Mono<OrderCreatedEvent> createOrder(OrderRequest request) {
78-
// Business logic - return value is automatically published
110+
// business logic — the returned event is published automatically
79111
return Mono.just(new OrderCreatedEvent(request.getId()));
80112
}
81113
}
82114

83115
@Component
84116
public class OrderEventHandler {
85117

86-
@EventListener(topic = "orders")
87-
public Mono<Void> onOrderCreated(EventEnvelope<OrderCreatedEvent> envelope) {
88-
// Handle the event
89-
return processOrder(envelope.getPayload());
118+
// Receives events from "orders"; eventTypes/condition support glob + SpEL filtering.
119+
@EventListener(destinations = "orders", eventTypes = "order.created")
120+
public Mono<Void> onOrderCreated(EventEnvelope envelope) {
121+
OrderCreatedEvent event = (OrderCreatedEvent) envelope.getPayload();
122+
return processOrder(event);
90123
}
91124
}
92125
```
93126

127+
You can also publish imperatively through the factory, which selects the transport for you:
128+
129+
```java
130+
import org.fireflyframework.eda.annotation.PublisherType;
131+
import org.fireflyframework.eda.publisher.EventPublisherFactory;
132+
133+
@Service
134+
public class PaymentService {
135+
136+
private final EventPublisherFactory publishers;
137+
138+
public PaymentService(EventPublisherFactory publishers) {
139+
this.publishers = publishers;
140+
}
141+
142+
public Mono<Void> emit(PaymentCaptured event) {
143+
// PublisherType.AUTO resolves KAFKA -> RABBITMQ -> POSTGRES -> APPLICATION_EVENT
144+
return publishers.getPublisher(PublisherType.AUTO)
145+
.publish(event, "payments");
146+
}
147+
}
148+
```
149+
150+
To send the same code over Apache Kafka, add `fireflyframework-eda-kafka` and configure a Kafka publisher (see below) — no code changes required.
151+
94152
## Configuration
95153

154+
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:
155+
96156
```yaml
97157
firefly:
98158
eda:
99-
enabled: true
100-
default-publisher-type: AUTO # AUTO chooses KAFKA → RABBITMQ → POSTGRES → APPLICATION_EVENT
101-
publishers:
159+
enabled: true # master switch for the whole module (default: true)
160+
default-publisher-type: AUTO # AUTO | APPLICATION_EVENT | KAFKA | RABBITMQ | POSTGRES | NOOP
161+
default-connection-id: default # connection key used when none is given
162+
default-destination: events # destination used when none is specified
163+
default-serialization-format: json # json | avro | protobuf
164+
default-timeout: 30s
165+
metrics-enabled: true
166+
health-enabled: true
167+
tracing-enabled: true
168+
169+
resilience:
102170
enabled: true
171+
circuit-breaker:
172+
enabled: true
173+
failure-rate-threshold: 50
174+
minimum-number-of-calls: 10
175+
sliding-window-size: 10
176+
wait-duration-in-open-state: 60s
177+
permitted-number-of-calls-in-half-open-state: 3
178+
retry:
179+
enabled: true
180+
max-attempts: 3
181+
wait-duration: 500ms
182+
exponential-backoff-multiplier: 2.0
183+
rate-limiter:
184+
enabled: false
185+
limit-for-period: 100
186+
limit-refresh-period: 1s
187+
timeout-duration: 5s
188+
189+
publishers:
190+
enabled: true # global publisher switch (default: false)
191+
application-event:
192+
enabled: true # in-JVM transport, always available in the core
193+
default-destination: application-events
194+
# --- requires the fireflyframework-eda-kafka adapter ---
103195
kafka:
104196
default:
105197
enabled: true
106198
bootstrap-servers: localhost:9092
199+
default-topic: events
200+
# --- requires the fireflyframework-eda-rabbitmq adapter ---
107201
rabbitmq:
108202
default:
109203
enabled: true
110204
host: localhost
111205
port: 5672
206+
username: guest
207+
password: guest
208+
default-exchange: events
209+
default-routing-key: event
210+
# --- requires the fireflyframework-eda-postgres adapter ---
112211
postgres:
113212
default:
114213
enabled: true
@@ -119,21 +218,33 @@ firefly:
119218
password: secret
120219
schema: public
121220
outbox-table: firefly_eda_outbox
122-
default-destination: events
123-
auto-create-schema: true # provision outbox table + NOTIFY trigger at startup
221+
auto-create-schema: true # provision outbox table + NOTIFY trigger at startup
222+
124223
consumer:
125-
enabled: true
224+
enabled: true # global consumer switch (default: false)
126225
group-id: my-service
226+
concurrency: 1
227+
retry:
228+
enabled: true
229+
max-attempts: 3
230+
initial-delay: 1s
231+
max-delay: 5m
232+
multiplier: 2.0
233+
application-event:
234+
enabled: true
127235
kafka:
128236
default:
129237
enabled: true
130238
bootstrap-servers: localhost:9092
239+
topics: events
240+
auto-offset-reset: earliest
131241
rabbitmq:
132242
default:
133243
enabled: true
134244
host: localhost
135245
port: 5672
136246
queues: events-queue
247+
prefetch-count: 10
137248
postgres:
138249
default:
139250
enabled: true
@@ -142,50 +253,50 @@ firefly:
142253
database: app
143254
username: app
144255
password: secret
145-
channels: events,order-events # destinations to LISTEN on
146-
polling-interval: 30s # NOTIFY-loss fallback poll cadence
147-
max-attempts: 3 # outbox row moves to DEAD_LETTER after N failures
256+
channels: events # destinations to LISTEN on
257+
polling-interval: 30s # NOTIFY-loss fallback poll cadence (0s disables polling)
258+
max-attempts: 3 # outbox row -> DEAD_LETTER after N failures
259+
batch-size: 50
148260
```
149261
150-
### PostgreSQL transport at a glance
151-
152-
- Each `publish()` performs a single `INSERT` into `firefly_eda_outbox`. A
153-
database trigger fires `pg_notify(channel, id)` for every inserted row.
154-
- The consumer holds a dedicated R2DBC connection that runs `LISTEN <channel>`
155-
for every subscribed destination. Notifications carry only the outbox row
156-
id so payloads can be arbitrarily large.
157-
- On dispatch, the listener pipeline either marks the row `PROCESSED` or
158-
increments `attempts`; once `attempts` reaches `max-attempts`, the row
159-
moves to `DEAD_LETTER` status.
160-
- A periodic poll (`polling-interval`) catches rows that slipped past the
161-
live channel (e.g., consumer offline at insert time, payload too large,
162-
connection reset). Set it to `0s` to disable polling.
163-
- Channel names are derived deterministically from destinations via the
164-
built-in mapper: non-alphanumeric characters become `_`, the result is
165-
lower-cased, prefixed with `firefly_eda_`, and truncated to fit
166-
PostgreSQL's 63-byte identifier limit with a stable hash suffix when
167-
needed.
262+
### Key properties
263+
264+
| Property | Default | Description |
265+
|----------|---------|-------------|
266+
| `firefly.eda.enabled` | `true` | Master switch for the entire EDA module. |
267+
| `firefly.eda.default-publisher-type` | `AUTO` | Transport used when none is specified. `AUTO` resolves `KAFKA → RABBITMQ → POSTGRES → APPLICATION_EVENT`. |
268+
| `firefly.eda.default-serialization-format` | `json` | Serializer used by default (`json`, `avro`, `protobuf`). |
269+
| `firefly.eda.default-timeout` | `30s` | Default timeout for publish operations. |
270+
| `firefly.eda.metrics-enabled` / `health-enabled` / `tracing-enabled` | `true` | Toggle Micrometer metrics, Actuator health, and tracing integration. |
271+
| `firefly.eda.publishers.enabled` | `false` | Global opt-in for all publishers. |
272+
| `firefly.eda.consumer.enabled` | `false` | Global opt-in for all consumers. |
273+
| `firefly.eda.consumer.group-id` | `firefly-eda` | Default consumer group id (used by group-based transports such as Kafka). |
274+
| `firefly.eda.resilience.*` | see above | Circuit breaker, retry and rate-limiter settings applied by `ResilientEventPublisher`. |
275+
276+
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.
168277

169278
## Documentation
170279

171-
Additional documentation is available in the [docs/](docs/) directory:
280+
- Framework documentation hub and module catalog: [fireflyframework/fireflyframework](https://github.com/fireflyframework)
281+
- Transport adapters: [eda-kafka](https://github.com/fireflyframework/fireflyframework-eda-kafka), [eda-rabbitmq](https://github.com/fireflyframework/fireflyframework-eda-rabbitmq), [eda-postgres](https://github.com/fireflyframework/fireflyframework-eda-postgres)
282+
283+
In-repo guides in [docs/](docs/):
172284

173285
- [Quickstart](docs/QUICKSTART.md)
174286
- [Architecture](docs/ARCHITECTURE.md)
175287
- [Configuration](docs/CONFIGURATION.md)
176288
- [Annotations](docs/ANNOTATIONS.md)
177-
- [Api Reference](docs/API_REFERENCE.md)
289+
- [API Reference](docs/API_REFERENCE.md)
178290
- [Publisher Types](docs/PUBLISHER_TYPES.md)
179291
- [Dynamic Topic Selection](docs/DYNAMIC_TOPIC_SELECTION.md)
180-
- [Eventlistener Examples](docs/EVENTLISTENER_EXAMPLES.md)
292+
- [EventListener Examples](docs/EVENTLISTENER_EXAMPLES.md)
181293
- [Custom Error Handling](docs/CUSTOM_ERROR_HANDLING.md)
182294
- [Examples](docs/EXAMPLES.md)
183295
- [Developer Guide](docs/DEVELOPER_GUIDE.md)
184-
- [Contributing](docs/CONTRIBUTING.md)
185296

186297
## Contributing
187298

188-
Contributions are welcome. Please read the [CONTRIBUTING.md](CONTRIBUTING.md) guide for details on our code of conduct, development process, and how to submit pull requests.
299+
Contributions are welcome. Please read the [CONTRIBUTING.md](docs/CONTRIBUTING.md) guide for details on our code of conduct, development process, and how to submit pull requests.
189300

190301
## License
191302

0 commit comments

Comments
 (0)