Spring Boot Skills
GitHub
Messaging

Event-Driven Messaging

Use when implementing Kafka, RabbitMQ, Pulsar, or JMS producers and consumers in Spring Boot 3. Covers event contracts, idempotency, retries, dead-letter handling, ordering, and outbox delivery.

View SKILL.md

Design for at-least-once delivery unless the complete system proves stronger semantics.

Event contract

  • Use an immutable event envelope with eventId, eventType, occurredAt, schemaVersion, and payload.
  • Treat published schemas as APIs. Add fields compatibly and never silently change meaning.
  • Put correlation and causation IDs in headers or the envelope.
  • Do not publish JPA entities or framework-specific serialization shapes.

Producer rules

  • Publish only after the originating state change is durable.
  • Use a transactional outbox when database state and broker publication must agree.
  • Use deterministic partition keys when per-aggregate ordering matters.
  • Configure delivery timeout and acknowledgements explicitly.

Consumer rules

  • Make handlers idempotent with a durable processed-event record or naturally idempotent write.
  • Keep the transaction boundary around the state change and idempotency marker.
  • Classify transient and permanent failures before configuring retries.
  • Bound retries with backoff, then route poison messages to a dead-letter destination.
  • Preserve the original event, failure reason, and attempt count for replay.

Testing

  • Test serialization compatibility and handler idempotency.
  • Use Testcontainers for broker integration tests.
  • Test duplicate, reordered, delayed, and poison messages.

Examples

  • See examples/good-consumer.java and examples/bad-consumer.java.

Gotchas

  • Agent assumes exactly-once delivery because the broker supports transactions - end-to-end side effects still need idempotency.
  • Agent retries validation failures forever - dead-letter permanent failures promptly.
  • Agent publishes inside a database transaction without an outbox - a crash can split state and event delivery.
  • Agent uses a random partition key - ordering for one aggregate is then lost.
  • Agent deserializes directly into a JPA entity - use a versioned event contract.
FROM THE REPOSITORY

Examples & templates

bad-consumer.javaexamples
Source on GitHub
@KafkaListener(topics = "orders.created")
void consume(OrderEntity event) {
    orders.save(event);
    emailClient.sendConfirmation(event.getCustomerEmail());
}
good-consumer.javaexamples
Source on GitHub
import java.util.UUID;

import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

@Component
final class OrderCreatedConsumer {

    private final TransactionTemplate transactionTemplate;
    private final ProcessedEvents processedEvents;
    private final Orders orders;

    OrderCreatedConsumer(
            TransactionTemplate transactionTemplate,
            ProcessedEvents processedEvents,
            Orders orders) {
        this.transactionTemplate = transactionTemplate;
        this.processedEvents = processedEvents;
        this.orders = orders;
    }

    @KafkaListener(topics = "orders.created", groupId = "order-projections")
    void consume(OrderCreated event) {
        transactionTemplate.executeWithoutResult(status -> {
            if (processedEvents.markIfNew(event.eventId())) {
                orders.apply(event);
            }
        });
    }
}

record OrderCreated(UUID eventId, UUID orderId, int schemaVersion) { }

interface ProcessedEvents {
    boolean markIfNew(UUID eventId);
}

interface Orders {
    void apply(OrderCreated event);
}

Verification scope

Compiled sources
No
Runtime checks
No

Coverage describes checked-in fixtures, not a guarantee for every snippet or project.

Coverage matrix & agent evidence
ADD TO YOUR AGENT

Install Boot 3 skills

claude plugin marketplace add rrezartprebreza/spring-boot-skills
claude plugin install spring-boot-3-skills@spring-boot-skills