Vert.x with Mutiny (Reactive)
Mutiny Uni/Multi over the callback API — composable reactive workflows.
# CLAUDE.md — Vert.x with Mutiny
## Why Mutiny
- **Mutiny** (`io.smallrye.mutiny:mutiny`) is a reactive library tightly integrated with Vert.x and Quarkus.
- Cleaner than callback Vert.x. More fluent than Vert.x `Future`. Reactive Streams compatible (interop with Reactor, RxJava).
- For new Vert.x code, prefer Mutiny or `Future`. Avoid the original callback API.
## Uni and Multi
- `Uni<T>` — 0..1 element with possible failure. Most use cases.
- `Multi<T>` — 0..N elements (a stream). For event streams, paginated reads, message subscriptions.
## Mutiny-flavored Vert.x
- Use `io.vertx.mutiny.*` packages — every Vert.x API has a Mutiny version returning `Uni`/`Multi`:
```java
io.vertx.mutiny.core.Vertx vertx = io.vertx.mutiny.core.Vertx.vertx();
io.vertx.mutiny.core.http.HttpClient client = vertx.createHttpClient();
Uni<String> body = client.request(GET, "https://example.com").flatMap(req -> req.send().flatMap(HttpClientResponse::body)).map(Buffer::toString);
```
- Don't import the non-Mutiny `io.vertx.core.*` types in Mutiny code. The signatures don't compose.
## Common operators
- `.map(fn)` — synchronous transform.
- `.onItem().transformToUni(fn)` — async transform returning a `Uni`. Equivalent to `flatMap`.
- `.onFailure().recoverWithItem(default)` — fallback on error.
- `.onFailure().retry().atMost(3)` — retry the upstream up to N times.
- `.ifNoItem().after(Duration.ofSeconds(5)).fail()` — timeout.
## Combining Unis
```java
Uni<Result> combined = Uni.combine().all().unis(uniA, uniB)
.with(Result::new);
```
- `combine().all()` for "wait for all". `combine().any()` for "first that completes".
- For collections: `Uni.combine().all().unis(listOfUnis).combinedWith(...)`.
## Subscribers
- Mutiny is **lazy**. The pipeline doesn't run until subscribed.
- In Vert.x Web with Mutiny: return `Uni<T>` from a handler — the framework subscribes for you.
- Manual: `uni.subscribe().with(item -> ..., failure -> ...)`.
## Backpressure (Multi)
- `Multi.createFrom().items(...)` for known finite streams.
- `Multi.createFrom().publisher(reactivePublisher)` for streams from another reactive lib.
- For backpressure: `.onOverflow().buffer(size)`, `.onOverflow().drop()`, etc. Choose based on what loss is acceptable.
## Error handling
- `onFailure()` selectors: `.onFailure(IOException.class).recoverWithUni(fallback)`.
- Always handle failures explicitly. Unhandled failures in `Uni` log a warning and disappear.
- Don't catch in the underlying lambda — let Mutiny carry the failure.
## Concurrency
- Mutiny respects the Vert.x event loop. Operators don't switch threads unless you ask.
- `.runSubscriptionOn(executor)` to subscribe on a specific executor.
- For blocking work inside a Uni chain: `.onItem().transformToUni(item -> Uni.createFrom().item(() -> blockingCall(item)).runSubscriptionOn(workerPool))`.
## Don't
- Don't mix Mutiny with raw Vert.x callbacks in the same chain. Convert at the boundary.
- Don't subscribe inside a handler — return the `Uni`.
- Don't `await().atMost(...)` on Unis in production code. That's blocking, defeating the purpose.
- Don't log inside `.map` lambdas — switch to `.invoke(item -> log.info(...))` for side-effect-only operators.
Other Java templates
Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.