Skip to content

DevServices

Outbox pattern in Quarkus

If you need to save data into two different systems, such as persist a data in the database and then notify another service about the local changes (through RabbitMQ, Kafka, ActiveMQ, etc.), this post is for you!

In this post, we will explore how to solve dual writes problem in a distributed systems using Quarkus.

Scenario

Imagine you have an order system, and every time an order is created or its status is changed, you need to notify another system to perform an action. This scenario is common when working with microservices. In the microservice world, each microservice needs to be cohesive, have low coupling, and be deployable independently. With these principles in mind, the only way to communicate with another microservice is through the network, which often leads to an increase in dual writes. Dual writes involve writing data into two different systems simultaneously. For example, writing into the database and also writing into ActiveMQ.

In the upcoming sections, we will see this in action and look at some common mistakes people make when trying to fix the dual writes issue.

Doing it with a monolith

In a monolith, it is very simple because most of the time we have just one write operation, where we are writing into one system (commonly database). Basically, what we need is:

I can do all things in a single transaction, right?

public CreateOrderOutput execute(final CreateOrderInput input) {
    Order order = new Order(); // 1. Create the order

    // Create another domain object from the Order instance // (1)

    QuarkusTransaction.requiringNew().run(() -> {
        // 2. Persist the `order`;
        // 3. Save the order details in the database for reporting purposes;
    })

    return new CreateOrderOutput(order);
}
  1. This other object, in a microservice architecture, would probably be in another microservice.