R2DBC in a nutshell

Bekir Durak
Yıldız Tech
Published in
2 min readDec 1, 2023

When building a highly available API, the traditional thread-per-request approach is inefficient. Dozens of years of wisdom tried to develop a workaround. But then, that workaround became a better solution for some existing problems. That solution was the reactive approach.

Instead of creating a thread for every request, the reactive approach uses a fixed thread pool for requests. So in this way, it optimizes CPU and memory usage. The application becomes more stable and elastic in hard circumcisions.

But when you develop a reactive system, you want to make everything reactive. Otherwise, your non-blocking API plans would collapse. You can’t use the advantages of reactive programming so your work would be wasted. To prevent this you should use reactive libraries for your dependencies.

Reactive landscape

Reactive Relational Database Connectivity (R2DBC) has been created to fill the gap between reactive JVM apps and DBs. It is designed as a Service Provider Interface so driver developers can implement it. Drivers can be customized for different DBs and reactive tech stacks. For example, the PostgreSQL r2dbc driver uses Project Reactor for reactive backend, but it could have been using a different one, like RxJava.

For developers who want to use the reactive stack for their work, a little bit of effort is required to implement it. But modern frameworks make it very easy to use a blocking stack. For example, let's look a Spring Data R2DBC vs. Spring Data JPA code:

// JPA
@Entity
@Data
public class Customer {

@Id private Long id;

private String firstname;
private String lastname;

}

public interface CustomerRepository extends CrudRepository<Customer, Long> {

List<Customer> findByLastname(String lastname);

}
// R2DBC
@Table(name = "customer")
@Data
public class Customer {

@Id private Long id;

private String firstname;
private String lastname;

}

public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {

Flux<Customer> findByLastname(String lastname);

}

You can check the complete example here.

As you can see, at first look only some syntax changes can seen. But in the background, so much stuff does change. If curious take a look at further readings.

Further reading:

--

--