Skip to main content
FlowWarden dispatches MongoDB Change Stream events to handler methods annotated with @OnChange, @OnInsert, @OnUpdate, @OnDelete, or @OnReplace. These annotations go on methods inside a @ChangeStream class.

Overview

There are two types of handler annotations:

Dispatch Priority

When an event arrives, FlowWarden resolves the handler in this order:
  1. Typed handler matching the operation type (@OnInsert for INSERT, @OnUpdate for UPDATE, etc.)
  2. @OnChange fallback — invoked for every operation that is not covered by a typed handler, including DROP and INVALIDATE
Typed handlers always take priority. @OnChange is only invoked for operation types that have no dedicated typed handler in the class.

@OnChange

Generic handler for all Change Stream event types. Acts as a catch-all when no typed handler matches.
Rules:
  • Only one @OnChange method is allowed per @ChangeStream class.
  • The annotation takes no attributes — it is a pure catch-all.
  • Signature must use the CONTEXT_ONLY style — void handle(ChangeStreamContext ctx) or Mono<Void> handle(ChangeStreamContext ctx).
To handle a specific subset of operations, declare the corresponding typed handlers (e.g. @OnInsert and @OnUpdate) — either on separate methods or stacked on a single method delegating to a shared private helper. See Combining Annotations on the Same Method below.

Typed Handlers

@OnInsert

Called when a new document is inserted into the collection.

@OnUpdate

Called when an existing document is updated.
For UPDATE events, fullDocument may be null by default — MongoDB only sends the change delta. To get the full document on updates, enable UPDATE_LOOKUP on the MongoDB Change Stream options.

@OnDelete

Called when a document is deleted.
For DELETE events, the full document is always null. If you use a document-typed signature like void onDelete(Order doc, ...), the doc parameter will be null. Prefer the CONTEXT_ONLY signature and use ctx.getDocumentKey() to identify the deleted document.

@OnReplace

Called when a document is replaced entirely (e.g. via MongoTemplate.save() on an existing document).

Rules for Typed Handlers

  • At most one method per typed annotation per class (e.g. you cannot have two @OnInsert methods)
  • If no typed handler matches the event, @OnChange is used as fallback (if present)
  • If neither a typed handler nor @OnChange matches, the event is silently skipped

Combining Annotations on the Same Method

You can place multiple typed annotations on a single method to handle several operation types with the same logic:
This is the recommended way to scope a handler to a specific subset of operation types — typed annotations make the intent explicit, and they take priority over @OnChange if a catch-all is also declared in the same class.
This is especially convenient when used with @Filter, since @Filter requires that all covered operation types have a fullDocument. Combining @OnInsert and @OnUpdate is safe — both have a full document available.
Combining annotations with and without fullDocument on the same method (e.g. @OnUpdate @OnDelete) emits a warning at startup. DELETE, DROP, and INVALIDATE events have no full document — the document parameter will be null for those events. Either use separate methods or handle the null case explicitly in your handler.
@OnChange is a catch-all only — it cannot be narrowed to specific operation types. To target a subset (e.g. INSERT and UPDATE only), declare typed handlers that delegate to a shared helper:
This keeps the dispatch decision in annotations (visible to the framework) and the logic in plain Java, instead of hiding the operation filter inside an annotation attribute.

Supported Signatures

Parameter Styles

Typed handler methods (@OnInsert, @OnUpdate, @OnDelete, @OnReplace) support three parameter styles: @OnChange only supports CONTEXT_ONLY.
DOCUMENT_ONLY and DOCUMENT_AND_CONTEXT signatures require a concrete documentType on @ChangeStream (not Document.class). Otherwise, startup fails with a validation error.

Return Types — Mode Exclusivity

Handler methods must use the return type that matches the configured flowwarden.default-mode:
Mixing return types is not allowed. All handler methods in an application must consistently use either void (imperative) or Mono<Void> (reactive), matching the flowwarden.default-mode. A mismatch causes a BeanCreationException at startup.
This gives the following full signature matrix:
Any other return type (e.g. CompletableFuture, Flux, String) is rejected at startup.

Examples

Typed Handlers with @OnChange Fallback

This example handles INSERT, UPDATE, and DELETE with typed handlers, and uses @OnChange as a fallback for REPLACE events.

Combined Annotations with @Filter

From the sample-spring-mvc module — a handler that reacts to both INSERT and UPDATE with a client-side filter:
This works because both INSERT and UPDATE events have a fullDocument available, which is required by @Filter. Combining @OnInsert @OnDelete with @Filter would fail at startup because DELETE events have no full document.

Minimal — Single @OnChange

The simplest form: one handler for all event types.

Event Capture for Testing

From the flowwarden-samples project — a reusable handler that captures events for test assertions:

Validation Errors

FlowWarden validates handler methods at startup. Here are the common errors:

See Also

@ChangeStream

The parent annotation that declares a Change Stream handler class.

Handler Signatures

Detailed reference for all supported method signatures.

ChangeStreamContext

The context object passed to every handler — access document, metadata, and operations.

@Filter

Server-side filtering to reduce the events reaching your handlers.