Skip to main content

Prerequisites

Java 17+

Spring Boot 3.x requires Java 17 as a minimum.

Spring Boot 3.x

Spring MVC or Spring WebFlux — both are supported.

MongoDB Replica Set

Change Streams require a Replica Set. A single-node RS is fine for local dev.
MongoDB must be running as a Replica Set — Change Streams are not available on standalone instances. For local development, start MongoDB with --replSet rs0 or use a Docker Compose setup.

Step 1 — Add the dependency

<dependency>
    <groupId>io.flowwarden</groupId>
    <artifactId>flowwarden-stream-core</artifactId>
    <version>1.0.0-rc.3</version>
</dependency>
implementation 'io.flowwarden:flowwarden-stream-core:1.0.0-rc.3'
No additional dependencies needed. FlowWarden uses only Spring Data MongoDB, which is already on your classpath via spring-boot-starter-data-mongodb.

Using the BOM (optional)

If you also pull flowwarden-stream-core-testkit (for backend implementors) or future satellite modules, import the flowwarden-bom in your dependencyManagement to keep versions aligned:
Maven
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.flowwarden</groupId>
      <artifactId>flowwarden-bom</artifactId>
      <version>1.0.0-rc.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.flowwarden</groupId>
    <artifactId>flowwarden-stream-core</artifactId>
  </dependency>
</dependencies>

Step 2 — Configure your application

Add the following to your application.yml:
flowwarden:
  default-mode: IMPERATIVE

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/mydb?replicaSet=rs0
flowwarden.default-mode is optional — it defaults to IMPERATIVE when omitted. Only set it explicitly if you need the reactive mode.

Step 3 — Enable FlowWarden

Add @EnableFlowWarden to your main application class:
import io.flowwarden.stream.annotation.EnableFlowWarden;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableFlowWarden
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
This activates the FlowWarden auto-configuration. @ChangeStream classes are discovered through Spring’s standard component scanning — @ChangeStream is meta-annotated with @Component, so no extra @ComponentScan configuration is needed.

Step 4 — Create your first handler

Declare a Spring bean annotated with @ChangeStream and add a handler method:
import io.flowwarden.stream.ChangeStreamContext;
import io.flowwarden.stream.annotation.ChangeStream;
import io.flowwarden.stream.annotation.OnChange;

@ChangeStream(documentType = Order.class)
public class OrderStream {

    @OnChange
    void handleOrderChange(ChangeStreamContext<Order> ctx) {
        System.out.println(ctx.summary());
    }
}
import io.flowwarden.stream.ChangeStreamContext;
import io.flowwarden.stream.annotation.ChangeStream;
import io.flowwarden.stream.annotation.OnChange;
import reactor.core.publisher.Mono;

@ChangeStream(documentType = Order.class)
public class OrderStream {

    @OnChange
    Mono<Void> handleOrderChange(ChangeStreamContext<Order> ctx) {
        return Mono.fromRunnable(() -> System.out.println(ctx.summary()));
    }
}
Where Order is a Spring Data @Document class:
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "orders")
public class Order {

    @Id
    private String id;
    private String customerEmail;
    private String status;
    // getters, setters...
}
When documentType is set, FlowWarden infers the collection name from the @Document annotation (or the decapitalized class name if none). No need to repeat it in @ChangeStream(collection = "orders").

Step 5 — Run it

Start your application. On boot, you will see a log line for each discovered stream:
INFO  i.f.s.i.d.ChangeStreamBeanPostProcessor - Discovered Change Stream 'order-stream' on collection 'orders' (handlers: @OnChange=handleOrderChange, checkpoint: false, retryPolicy: false, dlq: false, onError: false)
Then insert a document in the orders collection — your handler fires immediately.
MongoDB shell
db.orders.insertOne({ customerEmail: "alice@example.com", status: "PENDING", total: 49.99 })

What’s next?

How it Works

Understand the startup lifecycle and the event processing pipeline

Typed handlers

Use @OnInsert, @OnUpdate, @OnDelete for operation-specific logic

Checkpoint & Resume

Survive restarts without missing or replaying events

Filtering Events

Push aggregation pipelines to MongoDB or filter in Java