The Java Reactive Streams APIs have been part of the JDK since Java 9.
It is about time for the modern Java ecosystem to migrate away from the legacy APIs (
org.reactivestreams:reactive-streams Maven coordinates) and adopt the interfaces in
Migration of isomorphic APIs
The good news is that the legacy and the
Flow APIs are isomorphic.
One option is to perform string replacements to move from one API to the other, but an IDE like IntelliJ can help you with API migrations (see
Refactor > Migrate Packages and Classes):
The bad news is that moving from one API to the other could be a breaking change for your own code bases.
If your code relies on a high-level implementation of Reactive Streams then the change will be mostly transparent at the source code level.
For instance the Hibernate Reactive library uses Mutiny and none of the low-level Reactive Streams types such as
Publisher, hence the migration of Mutiny to the JDK
Flow APIs requires no change in Hibernate Reactive.
By contrast RESTEasy Reactive does support exposing endpoints using
org.reactivestreams.Publisher<T> return types (and not just, say,
Multi<T> from Mutiny), so the migration requires more work than just bumping a dependency version.
- In many cases one can simply perform an API migration.
- In some cases such as that of RESTEasy Reactive there needs to be a transition period where both the legacy and
Flowtypes will be supported.
- In some other cases such as using a library with a dependency to the legacy APIs, type adaptation will need to be made.
Flow / Legacy type adapters
You might as well use the adapters that I developed and maintain as part of Mutiny Zero.
Suppose that you have a library that has yet to migrate to
You can easily turn a
Publisher<T> into a
Publisher<String> rsPublisher = connect("foo"); // ... where 'connect' returns a Publisher<String> Flow.Publisher<String> flowPublisher = AdaptersToFlow.publisher(rsPublisher);
Type adapters exist for the 4 interfaces of Reactive Streams, and they have virtually no cost.
Passing the Reactive Streams TCK
While the Reactive Streams APIs are fairly simple, the evil is in the protocol and semantics. This is why publishers, processors and subscribers need to pass the Reactive Streams TCK.
There is fortunately a
Flow variant of the TCK, so if you have implemented Reactive Streams the changes will be minimal as you transition to
First, the TCK dependency Maven coordinates will become
Next, you will need to move your test classes from
org.reactivestreams.tck.PublisherVerification<T> as a base class to
The rest of your TCK test code will be the same, except that some method names have
Flow in them:
You can see that in one of the test cases from Mutiny Zero.
Migrating to the JDK
Flow APIs is important for the modern Java ecosystem, especially as Reactive Streams APIs have been part of the JDK since Java 9.
The migration is fairly transparent for application developers as they are unlikely to be directly using the low-level Reactive Streams types. This is instead the duty of frameworks, libraries and drivers to do this transition and impose one less dependency in application stacks.
The migration in itself isn’t too hard to perform as types are isomorphic, but there is an inevitable transition period for stacks where multiple dependencies need to be aligned past Java 8 and on top of the JDK
Type adapters represent a virtually no-cost solution when alignment is not possible yet.
The most important part for Reactive Streams implementers remains its TCK as the guardian of interoperability between various libraries.
As the TCK already ships with a
Flow variant, migrating away from the legacy APIs won’t break the behavior and interoperability of Reactive Streams implementations.