Skip to content

13.1.2 Defining reactive repositories

In chapters 3 and 4, we defined our repositories as interfaces that extend Spring Data’s CrudRepository interface. But that base repository interface dealt with singular objects and Iterable collections. In contrast, we’d expect that a reactive repository would deal in Mono and Flux objects.

That’s why Spring Data offers ReactiveCrudRepository for defining reactive repositories. ReactiveCrudRepository operates very much like CrudRepository. To create a repository, define an interface that extends ReactiveCrudRepository, such as this:

java
package tacos.data;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import tacos.TacoOrder;

public interface OrderRepository
      extends ReactiveCrudRepository<TacoOrder, Long> {
}

On the surface, the only difference between this OrderRepository and the ones we defined in chapters 3 and 4 is that it extends ReactiveCrudRepository instead of CrudRepository. But what’s significantly different is that its methods return Mono and Flux types instead of a single TacoOrder or Iterable<TacoOrder>. Two examples include the findById() method, which returns a Mono<TacoOrder>, and findAll(), which returns a Flux<TacoOrder>.

To see how this reactive repository might work in action, suppose that you want to fetch all TacoOrder objects and print their delivery names to standard output. In that case, you might write some code like the next snippet.

Listing 13.4 Calling a reactive repository method

java
@Autowired
OrderRepository orderRepo;

...

orderRepository.findAll()
  .doOnNext(order -> {
    System.out.println(
      "Deliver to: " + order.getDeliveryName());
    })
  .subscribe();

Here, the call to findAll() returns a Flux<TacoOrder> on which we have added a doOnNext() to print the delivery name. Finally, the call to subscribe() kicks off the flow of data through the Flux.

In the Spring Data JDBC example from chapter 3, TacoOrder was the aggregate root, with Taco being a child in that aggregate. Therefore, Taco objects were persisted as part of a TacoOrder, and there was no need to define a repository dedicated to Taco persistence. But Spring Data R2DBC doesn’t support proper aggregate roots this way, so we’ll need a TacoRepository through which Taco objects are persisted. See the next listing for such a repository.

Listing 13.5 Persisting Taco objects with a reactive repository

java
package tacos.data;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import tacos.Taco;

public interface TacoRepository
      extends ReactiveCrudRepository<Taco, Long> {
}

As you can see, TacoRepository isn’t much different from OrderRepository. It extends ReactiveCrudRepository to give us reactive types when working with Taco persistence. There aren’t many surprises here.

On the other hand, IngredientRepository is slightly more interesting, as shown next.

Listing 13.6 Persisting Ingredient objects with a reactive repository

java
package tacos.data;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import reactor.core.publisher.Mono;
import tacos.Ingredient;

public interface IngredientRepository
      extends ReactiveCrudRepository<Ingredient, Long> {

  Mono<Ingredient> findBySlug(String slug);

}

As with our other two reactive repositories, IngredientRepository extends ReactiveCrudRepository. But because we might need a way to look up Ingredient objects based on a slug value, IngredientRepository includes a findBySlug() method that returns a Mono<Ingredient>.

Now let’s see how to write tests to verify that our repositories work.

Released under the MIT License.