Spring MVC is one of my favorite web frameworks. Next major release of the popular web framework brings some interesting features. Along with Spring MVC, Spring Web Flux is coming later this year and it is coming here to stay. Spring Web Flux will help us to build high throughput functional reactive microservices.

What is reactive programming?

Reactive programming is programming with asynchronous streams [1]. The magic two words here are asynchronous and stream. In reactive programming, everything is a stream. Unlike pull-centric Java 8 stream, reactive streams are push-centric. It uses the well known Observer Patern. Reactive programming is a wide topic and details are beyond the scope of this blog post. You can check the Reactive Streams, Reactive Manifesto, RxJava and this blog post.

Get Started

Prerequisites:

  • Java 8
  • MongoDB
  • Maven
  • IDE

In order to generate the project skeleton, go to http://start.spring.io address and choose “Generate a “maven” project with “Java” and Spring Boot “2.0.x”. As of writing this blog post, the latest version was 2.0.0 M2. In dependencies, section, choose Reactive Web and Reactive MongoDB. Then unzip the downloaded file and open with your favorite IDE. My personal preference is IntelliJ.

Currently, only MongoDB, Cassandra, and Redis are supported as reactive data stores. In this blog post, I will go with MongoDB.

Configuration

In order to make use of MongoDB’s async API’s, we need to enable the reactive MongoDB repositories. The default configuration will connect you to the local MongoDB instance on the default port.

@EnableReactiveMongoRepositories
public class MongoDBConfiguration extends AbstractReactiveMongoConfiguration {

    @Bean
    @Override
    public MongoClient mongoClient() {
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        return "reactive_todos";
    }
}

Model

Our Todo model will consist of an id field, title, creation and completion dates and a boolean completion flag.

@Document
public class Todo {

    @Id
    private BigInteger id;
    private String title;
    private boolean completed;
    private Date createdDate;
    private Date completedDate;

    public Todo() {
    }

    // Getters ...
    // Setters ...
}

You can check out Project Lombok in order to create clean data models. It can magically create constructors, getters, and setters.

Data Layer

public interface TodoRepository extends ReactiveCrudRepository<Todo, BigInteger> {
}

Creating this interface will magically generate common CRUD methods for us. The first generic argument is our domain model and the second is the type of the @Id annotated field. For MongoDB documents it can a String or a BigInteger.

Web Layer

The controller will be annotated with @RestController. Just like nonreactive rest applications.

@RestController
@RequestMapping("/todos")
public class TodoController {
  private TodoRepository todoRepository;

  @Autowired
  public TodoController(TodoRepository todoRepository) {
      this.todoRepository = todoRepository;
  }
}

Spring can inject dependencies in a constructor without @Autowired annotation but I prefer to declaring it explicitly.

Creating a Todo

In order to create a To-Do, we will use the following method.

@PostMapping("")
public Mono<Todo> create(@RequestBody Todo todo) {
    todo.setCreatedDate(new Date());
    return todoRepository.save(todo);
}

Start the application by using the IDE or from the command line with mvn spring-boot:run command. Using the command line you can add some todos:

curl -H "Content-Type: application/json" -X POST -d '{"title": "Create a REST API"}' http://localhost:8080/todos
curl -H "Content-Type: application/json" -X POST -d '{"title": "Write a blog post"}' http://localhost:8080/todos
curl -H "Content-Type: application/json" -X POST -d '{"title": "Learn some Java and Functional Programming"}' http://localhost:8080/todos

Listing all Todos

In order to list all todos, we will use the Flux class. Flux is just like Mono but there is a fundamental difference. A Mono can contain 0 or 1 elements in contrast to Flux can contain 0 to N elements. Flux is similar to a standard Java Stream or a List in that manner.

@GetMapping("")
public Flux<Todo> index() {
    return todoRepository.findAll();
}

Filter ToDos by Title

We are going to add a custom query to our TodoRepository. It will query our MongoDB database and return results as a Reactive Stream.

Add the following line to TodoRepository.

Flux<Todo> findAllByTitleContains(final Mono<String> title);

Do not try to implement this method. Just leave it as it is. Spring data package will automatically implement the required method from the meaning.

And add the following handler method to the controller.

@GetMapping("search/title/{key}")
public Flux<Todo> findByTitle(@PathVariable(name = "key") String key) {
    return todoRepository.findAllByTitleContains(Mono.just(key));
}

Now we can search Todos which contain the string in their titles.

curl localhost:8080/todos/search/title/blog

curl localhost:8080/todos/search/title/java

Completing a ToDo

I have always liked the domain driven approach. So let’s add a complete method to our domain object.

// Todo.java
public Todo complete() {
    this.setCompleted(true);
    this.setCompletedDate(new Date());
    return this;
}

We need to add a handler method to our controller.

@PatchMapping("complete/{id}")
public Mono<Todo> complete(@PathVariable(name = "id") BigInteger id) {
    return todoRepository.findById(id)
                         .map(Todo::complete)
                         .flatMap(todoRepository::save);
}

As you see, we are following the functional programming fashion.

In order to test the endpoint, you can use the following command.

curl -H "Content-Type: application/json" -X PATCH http://localhost:8080/todos/complete/27667845079056742421533148010

Just do not forget to replace the id section with one of your own todo ids.

Removing a Todo

It is very similar what we have done in the previous example. We will use flatMap function to call the delete method of the reactive repository.

@DeleteMapping("{id}")
public Mono<Void> delete(@PathVariable(name = "id") BigInteger id) {
    return todoRepository.findById(id).flatMap(todoRepository::delete);
}

For testing:

curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/todos/27667845079056742421533148010

Summary

Spring Boot 2.0 is coming with bunch of new features such as Functional Reactive Programming concepts. I am very excited to see what comes next. I hope this article helped to understand the basics of the reactive data repositories and reactive web applications.

You can check the full source code in this repo.

  1. https://gist.github.com/staltz/868e7e9bc2a7b8c1f754