Enterprise Java

Pub/Sub with Spring Boot and Dapr

Microservices communication is crucial in distributed systems, and the publish-subscribe (pub/sub) pattern offers a loosely coupled and scalable solution. With Dapr and Spring Boot, you can implement flexible and portable pub/sub messaging with minimal boilerplate code and maximum interoperability. Let us delve into understanding Spring Boot Dapr Pub/Sub messaging and how it enables flexible communication between microservices.

1. What is Spring Dapr?

Dapr (Distributed Application Runtime) is a portable, event-driven runtime that enables developers to build resilient, microservice-based applications. It provides a rich set of building blocks, including service invocation, state management, secrets handling, bindings, and the publish/subscribe messaging pattern.

The Pub/Sub API in Dapr allows services to publish events to a topic and subscribe to specific topics, enabling asynchronous, decoupled communication between services. This approach is especially useful in cloud-native applications where flexibility and scalability are essential.

With Spring Boot’s robust ecosystem and Dapr’s infrastructure abstraction, developers can create highly modular microservices where publishers and subscribers are unaware of each other’s existence — they simply rely on topics. This helps in achieving loose coupling, better testability, and effortless scalability.

2. Setting up Dapr on Docker Desktop

To use Dapr for local development, you can install and initialize it on your machine using Docker Desktop. This guide will walk you through the setup.

2.1 Install Dapr CLI

The Dapr CLI is a command-line tool that allows you to interact with the Dapr runtime. It simplifies tasks like initializing the Dapr environment, checking the status, and running applications with Dapr sidecars. To install the Dapr CLI on Unix-based systems (Linux/macOS), run:

curl -fsSL https://get.dapr.io | /bin/bash

For Windows users, it’s recommended to use PowerShell:

iwr -useb https://get.dapr.io/install.ps1 | iex

For more installation options, refer to the official Dapr CLI documentation.

2.2 Initialize Dapr with Docker

Once the CLI is installed, you can initialize Dapr in self-hosted mode using Docker containers. This will pull and configure the Dapr sidecar, along with Redis (used as the default state store and pub/sub component).

dapr init

This command sets up the following Docker containers:

  • dapr_placement: Dapr’s placement service for actor-based services
  • redis: Used for state management and pub/sub by default
  • zipkin: Used for distributed tracing

To verify the containers are running, you can also run:

docker ps

2.3 Verify Installation

To confirm that Dapr was initialized successfully, you can run:

dapr status

You should see a list of components like the Dapr dashboard, Redis, placement service, etc., all marked as running. If you prefer a graphical interface, Dapr provides a web-based dashboard that can be started using:

dapr dashboard

This opens the dashboard in your browser at http://localhost:8080, allowing you to monitor apps, sidecars, and components.

3. Code Example

3.1 Project Setup

We start with a basic Spring Boot application. Add the following dependencies in your pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>io.dapr</groupId>
        <artifactId>dapr-sdk-springboot</artifactId>
        <version>latest__jar__version</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

3.2 Publisher Code

Let’s look at two different ways to implement a publisher with Spring Boot and Dapr.

3.2.1 Using DaprClient

The DaprClient is the low-level client provided by the Dapr SDK. In this example, we inject DaprClient into the controller, expose a POST endpoint that takes a topic and message body, and use publishEvent to send the event to the Dapr pub/sub system.

@RestController
@RequestMapping("/publish")
public class MessagePublisher {

    private final DaprClient daprClient;

    public MessagePublisher(DaprClient daprClient) {
        this.daprClient = daprClient;
    }

    @PostMapping("/{topic}")
    public Mono<Void> publishMessage(@PathVariable String topic, @RequestBody Map<String, String> body) {
        System.out.println("Publishing to topic: " + topic + ", message: " + body);
        return daprClient.publishEvent("pubsub", topic, body);
    }
}

3.2.2 Using DaprMessagingTemplate

DaprMessagingTemplate offers a Spring-style abstraction to send messages. The DaprMessagingTemplate wraps DaprClient with a familiar Spring API. The controller publishes messages by calling publish() directly, making this method ideal when working in a traditional Spring messaging model.

class PubSubConfig {
    @Bean
    public DaprMessagingTemplate daprMessagingTemplate(DaprClient daprClient) {
        return new DaprMessagingTemplate(daprClient);
    }
}

@RestController
@RequestMapping("/template-publish")
public class TemplatePublisher {

    private final DaprMessagingTemplate daprMessagingTemplate;

    public TemplatePublisher(DaprMessagingTemplate daprMessagingTemplate) {
        this.daprMessagingTemplate = daprMessagingTemplate;
    }

    @PostMapping("/{topic}")
    public void publishWithTemplate(@PathVariable String topic, @RequestBody Map<String, String> payload) {
        daprMessagingTemplate.publish("pubsub", topic, payload);
        System.out.println("Published using DaprMessagingTemplate: " + payload);
    }
}

3.3 Subscriber Code

The @Topic annotation is a Dapr annotation that binds this method to the mytopic topic on the pubsub component. Dapr invokes this endpoint with the message data whenever an event is published.

@RestController
public class MessageSubscriber {

    @Topic(name = "pubsub", topic = "mytopic")
    @PostMapping(path = "/messages")
    public void handleMessage(@RequestBody Map<String, String> message) {
        System.out.println("Received message: " + message);
    }
}

3.4 Application Class

This is the standard Spring Boot entry point.

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.5 Running the Services

To run the Spring Boot application, use the following Maven command which starts the application on the default port (8080):

mvn spring-boot:run

However, to fully leverage Dapr’s capabilities such as publish/subscribe messaging, run the Spring Boot app along with the Dapr sidecar using the dapr run command:

dapr run --app-id springapp --app-port 8080 --dapr-http-port 3500 -- mvn spring-boot:run

This command does the following:

  • --app-id springapp: Assigns a unique ID to the Dapr application instance. This ID is used for service discovery and pub/sub routing.
  • --app-port 8080: Specifies the port your Spring Boot app is running on so Dapr can communicate with it.
  • --dapr-http-port 3500: Sets the Dapr HTTP port that your application can use to call other services or access Dapr features like state management or publish/subscribe.
  • -- mvn spring-boot:run: The actual command used to start your Spring Boot application.

After running this command, Dapr will start a sidecar process alongside your application, enabling Dapr features such as service invocation, pub/sub messaging, and more. You should see logs from both Dapr and your Spring Boot app in the terminal.

3.6 Testing

Once both the Spring Boot application and Dapr sidecar are running successfully, you can test message publishing using curl commands. These simulate clients sending HTTP requests to the exposed REST endpoints of your Spring Boot service. Below are different test cases for verifying both direct DaprClient-based publishing and Spring-friendly DaprMessagingTemplate-based publishing.

3.6.1 Publish via DaprClient

This endpoint demonstrates publishing a message using the low-level DaprClient API. The request hits the /publish/{topic} endpoint, where the topic is passed as a path variable.

curl -X POST http://localhost:8080/publish/mytopic \
     -H "Content-Type: application/json" \
     -d '{"greeting":"Hello from Dapr!"}'

Here, the JSON payload {"greeting":"Hello from Dapr!"} is sent to the mytopic topic. This is handled internally by Dapr which forwards the message to any subscribed services.

3.6.2 Publish via DaprMessagingTemplate

This test shows publishing using the higher-level DaprMessagingTemplate abstraction, which integrates seamlessly with the Spring messaging model. The controller exposes the /template-publish/{topic} endpoint for this.

curl -X POST http://localhost:8080/template-publish/mytopic \
     -H "Content-Type: application/json" \
     -d '{"user":"Yatin", "message":"Template-based publish"}'

This sends a more structured message to the same topic. The DaprMessagingTemplate handles the serialization and forwarding of the event under the hood.

3.6.3 Expected Output:

If the subscriber is correctly configured using @Topic and listening on the same topic (mytopic), it should automatically receive the messages and log them on the console. You should see the following logs:

Received message: {greeting=Hello from Dapr!}
Received message: {user=Yatin, message=Template-based publish}

These log lines confirm that both publishing mechanisms—via DaprClient and DaprMessagingTemplate—are functioning correctly, and that Dapr successfully routed the messages to the subscriber service.

4. Conclusion

Spring Boot and Dapr provide a simple yet powerful way to implement pub/sub messaging across distributed systems. With Dapr’s infrastructure abstraction and Spring’s developer productivity, teams can focus more on business logic and less on wiring. By running Dapr with Docker and using the DaprClient and @Topic annotations, you can seamlessly create flexible and portable microservices that scale reliably.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button