2025 Event-Driven Microservices with Java Spring Boot: Kafka & RabbitMQ
Introduction
2025 Event-Driven Microservices with Java Spring Boot: Kafka & RabbitMQ

Introduction
Event-driven architecture (EDA) has become a cornerstone of modern microservices design, enabling systems to be more scalable, resilient, and loosely coupled. In this comprehensive guide, we’ll explore how to build event-driven microservices using Java Spring Boot with two popular message brokers: Apache Kafka and RabbitMQ.
What is Event-Driven Architecture?
Event-driven architecture is a software design pattern where services communicate through events rather than direct API calls. When something significant happens in one service (an event), it publishes that information, and other interested services can react to it asynchronously.
Key Benefits:
- Loose Coupling: Services don’t need to know about each other directly
- Scalability: Easy to scale individual components independently
- Resilience: Services can continue operating even if others are temporarily unavailable
- Flexibility: New services can subscribe to existing events without modifying producers
Kafka vs RabbitMQ: Choosing the Right Tool
Apache Kafka
Kafka is a distributed event streaming platform designed for high-throughput, fault-tolerant data pipelines. It excels at handling millions of events per second and retaining data for replay.
Best for:
- Event streaming and processing
- Log aggregation
- Real-time analytics
- High-throughput scenarios
- Event sourcing patterns
RabbitMQ
RabbitMQ is a traditional message broker that implements AMQP and supports various messaging patterns. It’s excellent for complex routing and reliable message delivery.
Best for:
- Complex routing requirements
- Request-reply patterns
- Priority queues
- Traditional messaging workflows
- Lower throughput but complex delivery semantics
Building Event-Driven Microservices with Spring Boot
1. Setting Up Dependencies
For Kafka:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
For RabbitMQ:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. Configuration
Kafka Configuration (application.yml):
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
group-id: my-group
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "*"
RabbitMQ Configuration (application.yml):
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
3. Implementing Event Producers
Kafka Producer Example:
@Service
@RequiredArgsConstructor
public class OrderEventProducer {
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
private static final String TOPIC = "order-events";
public void publishOrderCreated(OrderEvent event) {
kafkaTemplate.send(TOPIC, event.getOrderId(), event)
.thenAccept(result ->
log.info("Order event published: {}", event.getOrderId()))
.exceptionally(ex -> {
log.error("Failed to publish order event", ex);
return null;
});
}
}
RabbitMQ Producer Example:
@Service
@RequiredArgsConstructor
public class OrderEventPublisher {
private final RabbitTemplate rabbitTemplate;
private static final String EXCHANGE = "order-exchange";
private static final String ROUTING_KEY = "order.created";
public void publishOrderCreated(OrderEvent event) {
try {
rabbitTemplate.convertAndSend(EXCHANGE, ROUTING_KEY, event);
log.info("Order event published: {}", event.getOrderId());
} catch (Exception e) {
log.error("Failed to publish order event", e);
}
}
}
4. Implementing Event Consumers
Kafka Consumer Example:
@Service
@Slf4j
public class OrderEventConsumer {
@KafkaListener(topics = "order-events", groupId = "inventory-service")
public void consumeOrderEvent(OrderEvent event) {
log.info("Received order event: {}", event.getOrderId());
// Process the event
updateInventory(event);
}
private void updateInventory(OrderEvent event) {
// Business logic to update inventory
log.info("Inventory updated for order: {}", event.getOrderId());
}
}
RabbitMQ Consumer Example:
@Service
@Slf4j
public class OrderEventListener {
@RabbitListener(queues = "inventory-queue")
public void handleOrderEvent(OrderEvent event) {
log.info("Received order event: {}", event.getOrderId());
// Process the event
processOrder(event);
}
private void processOrder(OrderEvent event) {
// Business logic
log.info("Order processed: {}", event.getOrderId());
}
}
5. Event Model
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderEvent {
private String orderId;
private String customerId;
private LocalDateTime timestamp;
private OrderStatus status;
private List<OrderItem> items;
private BigDecimal totalAmount;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {
private String productId;
private Integer quantity;
private BigDecimal price;
}
public enum OrderStatus {
CREATED, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}
Advanced Patterns
1. Event Sourcing
Store all changes as a sequence of events rather than just the current state. This provides a complete audit trail and enables time travel debugging.
@Service
public class OrderEventStore {
public void saveEvent(DomainEvent event) {
// Store event in event store
eventRepository.save(event);
// Publish to event bus
eventPublisher.publish(event);
}
public List<DomainEvent> getEventsByAggregateId(String aggregateId) {
return eventRepository.findByAggregateId(aggregateId);
}
}
2. CQRS (Command Query Responsibility Segregation)
Separate read and write models to optimize for different use cases.
// Command side - writes
@Service
public class OrderCommandService {
public void createOrder(CreateOrderCommand command) {
Order order = new Order(command);
orderRepository.save(order);
eventPublisher.publish(new OrderCreatedEvent(order));
}
}
// Query side - reads
@Service
public class OrderQueryService {
public OrderView getOrder(String orderId) {
return orderViewRepository.findById(orderId);
}
}
3. Saga Pattern
Manage distributed transactions across microservices using a sequence of local transactions coordinated by events.
@Service
public class OrderSaga {
@KafkaListener(topics = "order-events")
public void handleOrderCreated(OrderCreatedEvent event) {
// Step 1: Reserve inventory
inventoryService.reserveItems(event.getItems());
}
@KafkaListener(topics = "inventory-events")
public void handleInventoryReserved(InventoryReservedEvent event) {
// Step 2: Process payment
paymentService.processPayment(event.getOrderId());
}
@KafkaListener(topics = "payment-events")
public void handlePaymentProcessed(PaymentProcessedEvent event) {
// Step 3: Confirm order
orderService.confirmOrder(event.getOrderId());
}
}
Best Practices
1. Idempotency
Ensure consumers can handle duplicate messages safely:
@Service
public class IdempotentOrderConsumer {
private final Set<String> processedEvents = ConcurrentHashMap.newKeySet();
@KafkaListener(topics = "order-events")
public void consumeOrder(OrderEvent event) {
if (processedEvents.contains(event.getEventId())) {
log.info("Event already processed: {}", event.getEventId());
return;
}
processOrder(event);
processedEvents.add(event.getEventId());
}
}
2. Error Handling and Dead Letter Queues
Kafka DLQ Configuration:
@Configuration
public class KafkaErrorConfig {
@Bean
public ConcurrentKafkaListenerContainerFactory<String, OrderEvent>
kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, OrderEvent> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setCommonErrorHandler(
new DefaultErrorHandler(
new DeadLetterPublishingRecoverer(kafkaTemplate()),
new FixedBackOff(1000L, 3L)
)
);
return factory;
}
}
RabbitMQ DLQ Configuration:
@Configuration
public class RabbitMQConfig {
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order-queue")
.withArgument("x-dead-letter-exchange", "dlx-exchange")
.withArgument("x-dead-letter-routing-key", "order.dlq")
.build();
}
}
3. Monitoring and Observability
@Aspect
@Component
@Slf4j
public class EventMonitoringAspect {
private final MeterRegistry meterRegistry;
@Around("@annotation(KafkaListener)")
public Object monitorKafkaConsumer(ProceedingJoinPoint joinPoint) throws Throwable {
Timer.Sample sample = Timer.start(meterRegistry);
try {
Object result = joinPoint.proceed();
sample.stop(Timer.builder("kafka.consumer.duration")
.tag("method", joinPoint.getSignature().getName())
.register(meterRegistry));
return result;
} catch (Exception e) {
meterRegistry.counter("kafka.consumer.errors",
"method", joinPoint.getSignature().getName()).increment();
throw e;
}
}
}
Testing Event-Driven Systems
Integration Testing with Testcontainers
@SpringBootTest
@Testcontainers
class OrderEventIntegrationTest {
@Container
static KafkaContainer kafka = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka:7.4.0")
);
@DynamicPropertySource
static void kafkaProperties(DynamicPropertyRegistry registry) {
registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers);
}
@Test
void shouldPublishAndConsumeOrderEvent() {
OrderEvent event = new OrderEvent("order-123", "customer-456");
orderEventProducer.publishOrderCreated(event);
await().atMost(Duration.ofSeconds(5))
.until(() -> orderEventConsumer.getProcessedEvents().contains("order-123"));
}
}
Conclusion
Event-driven microservices with Spring Boot, Kafka, and RabbitMQ provide a powerful foundation for building scalable, resilient distributed systems. The choice between Kafka and RabbitMQ depends on your specific requirements: use Kafka for high-throughput event streaming and RabbitMQ for complex routing scenarios.
Key takeaways include implementing proper error handling, ensuring idempotency, using patterns like CQRS and Saga for complex workflows, and maintaining comprehensive monitoring. With these practices in place, you’ll be well-equipped to build robust event-driven architectures.
메타데이터
- post_id
- 84d028890a6d
- slug
- 2025-event-driven-microservices-with-java-spring-boot-kafka-rabbitmq-84d028890a6d
- url
- https://medium.com/@anandjeyaseelan10/2025-event-driven-microservices-with-java-spring-boot-kafka-rabbitmq-84d028890a6d
- canonical_url
- https://medium.com/@anandjeyaseelan10/2025-event-driven-microservices-with-java-spring-boot-kafka-rabbitmq-84d028890a6d
- author_url
- https://medium.com/@anandjeyaseelan10
- status
- ok
- fetched_at
- 2026-06-09 15:37:30