spring-boot-event-driven-patterns

$npx mdskill add giuseppe-trisciuoglio/developer-kit/spring-boot-event-driven-patterns

Implement event-driven patterns in Spring Boot with Kafka and outbox.

  • Solves reliable async messaging and domain event publishing in microservices.
  • Depends on Spring Boot, Spring Kafka, and Spring Cloud Stream.
  • Decides based on event type, transactional boundaries, and outbox table.
  • Delivers code for domain events, listeners, Kafka producers, and outbox pattern.

SKILL.md

.github/skills/spring-boot-event-driven-patternsView on GitHub ↗
---
name: spring-boot-event-driven-patterns
description: Provides Event-Driven Architecture (EDA) patterns for Spring Boot — creates domain events, configures ApplicationEvent and @TransactionalEventListener, sets up Kafka producers and consumers, and implements the transactional outbox pattern for reliable distributed messaging. Use when implementing event-driven systems in Spring Boot, setting up async messaging with Kafka, publishing domain events from DDD aggregates, or needing reliable event publishing with the outbox pattern.
allowed-tools: Read, Write, Edit, Bash
---

# Spring Boot Event-Driven Patterns

## Overview

Implement Event-Driven Architecture (EDA) patterns in Spring Boot 3.x using domain events, ApplicationEventPublisher, `@TransactionalEventListener`, and distributed messaging with Kafka and Spring Cloud Stream.

## When to Use

- Implementing event-driven microservices with Kafka messaging
- Publishing domain events from aggregate roots in DDD architectures
- Setting up transactional event listeners that fire after database commits
- Adding async messaging with producers and consumers via Spring Kafka
- Ensuring reliable event delivery using the transactional outbox pattern
- Replacing synchronous calls with event-based communication between services

## Quick Reference

| Concept | Description |
|---------|-------------|
| **Domain Events** | Immutable events extending `DomainEvent` base class with eventId, occurredAt, correlationId |
| **Event Publishing** | `ApplicationEventPublisher.publishEvent()` for local, `KafkaTemplate` for distributed |
| **Event Listening** | `@TransactionalEventListener(phase = AFTER_COMMIT)` for reliable handling |
| **Kafka** | `@KafkaListener(topics = "...")` for distributed event consumption |
| **Spring Cloud Stream** | Functional programming model with `Consumer` beans |
| **Outbox Pattern** | Atomic event storage with business data, scheduled publisher |

## Examples

### Monolithic to Event-Driven Refactoring

**Before (Anti-Pattern):**
```java
@Transactional
public Order processOrder(OrderRequest request) {
    Order order = orderRepository.save(request);
    inventoryService.reserve(order.getItems()); // Blocking
    paymentService.charge(order.getPayment()); // Blocking
    emailService.sendConfirmation(order); // Blocking
    return order;
}
```

**After (Event-Driven):**
```java
@Transactional
public Order processOrder(OrderRequest request) {
    Order order = Order.create(request);
    orderRepository.save(order);

    // Publish event after transaction commits
    eventPublisher.publishEvent(new OrderCreatedEvent(order.getId(), order.getItems()));

    return order;
}

@Component
public class OrderEventHandler {
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleOrderCreated(OrderCreatedEvent event) {
        // Execute asynchronously after the order is saved
        inventoryService.reserve(event.getItems());
        paymentService.charge(event.getPayment());
    }
}
```

See [examples.md](references/examples.md) for complete working examples.

## Instructions

### 1. Design Domain Events

Create immutable event classes extending a base `DomainEvent` class:

```java
public abstract class DomainEvent {
    private final UUID eventId;
    private final LocalDateTime occurredAt;
    private final UUID correlationId;
}

public class ProductCreatedEvent extends DomainEvent {
    private final ProductId productId;
    private final String name;
    private final BigDecimal price;
}
```

See [domain-events-design.md](references/domain-events-design.md) for patterns.

### 2. Publish Events from Aggregates

Add domain events to aggregate roots, publish via `ApplicationEventPublisher`:

```java
@Service
@Transactional
public class ProductService {
    public Product createProduct(CreateProductRequest request) {
        Product product = Product.create(request.getName(), request.getPrice(), request.getStock());
        repository.save(product);

        product.getDomainEvents().forEach(eventPublisher::publishEvent);
        product.clearDomainEvents();

        return product;
    }
}
```

See [aggregate-root-patterns.md](references/aggregate-root-patterns.md) for DDD patterns.

### 3. Handle Events Transactionally

Use `@TransactionalEventListener` for reliable event handling:

```java
@Component
public class ProductEventHandler {
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void onProductCreated(ProductCreatedEvent event) {
        notificationService.sendProductCreatedNotification(event.getName());
    }
}
```

**Validate:** Confirm the event handler fires only after the transaction commits by checking that the database state is committed before the handler executes.

See [event-handling.md](references/event-handling.md) for handling patterns.

### 4. Configure Kafka Infrastructure

Configure KafkaTemplate for publishing, `@KafkaListener` for consuming:

```yaml
spring:
  kafka:
    bootstrap-servers: localhost:9092
    producer:
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
```

**Validate:** Send a test event via `KafkaTemplate` and confirm it appears in the consumer logs before proceeding to production patterns.

See [dependency-setup.md](references/dependency-setup.md) and [configuration.md](references/configuration.md).

### 5. Implement Outbox Pattern

Create `OutboxEvent` entity for atomic event storage:

```java
@Entity
public class OutboxEvent {
    private UUID id;
    private String aggregateId;
    private String eventType;
    private String payload;
    private LocalDateTime publishedAt;
}
```

**Validate:** Confirm the scheduled processor picks up pending events by checking the `publishedAt` timestamp is set after the scheduled run.

Scheduled processor publishes pending events. See [outbox-pattern.md](references/outbox-pattern.md).

### 6. Handle Failure Scenarios

Implement retry logic, dead-letter queues, idempotent handlers:

```java
@RetryableTopic(attempts = "3")
@KafkaListener(topics = "product-events")
public void handleProductEvent(ProductCreatedEventDto event) {
    orderService.onProductCreated(event);
}
```

**Validate:** Confirm messages reach the dead-letter topic after exhausting retries before moving to observability.

### 7. Add Observability

Enable Spring Cloud Sleuth for distributed tracing, monitor metrics.

## Best Practices

- **Use past tense naming**: `ProductCreated` (not `CreateProduct`)
- **Keep events immutable**: All fields should be final
- **Include correlation IDs**: For tracing events across services
- **Use AFTER_COMMIT phase**: Ensures events are published after successful database transaction
- **Implement idempotent handlers**: Handle duplicate events gracefully
- **Add retry mechanisms**: For failed event processing with exponential backoff
- **Implement dead-letter queues**: For events that fail processing after retries
- **Log all failures**: Include sufficient context for debugging
- **Make handlers order-independent**: Event ordering is not guaranteed in distributed systems
- **Batch event processing**: When handling high volumes
- **Monitor event latencies**: Set up alerts for slow processing

## References

- **[dependency-setup.md](references/dependency-setup.md)** — Maven/Gradle dependencies
- **[configuration.md](references/configuration.md)** — Kafka and Spring Cloud Stream configuration
- **[domain-events-design.md](references/domain-events-design.md)** — Domain event design patterns
- **[aggregate-root-patterns.md](references/aggregate-root-patterns.md)** — Aggregate root with event publishing
- **[event-publishing.md](references/event-publishing.md)** — Local and distributed event publishing
- **[event-handling.md](references/event-handling.md)** — Event handling and consumption patterns
- **[outbox-pattern.md](references/outbox-pattern.md)** — Transactional outbox pattern for reliability
- **[testing-strategies.md](references/testing-strategies.md)** — Unit and integration testing approaches
- **[examples.md](references/examples.md)** — Complete working examples
- **[event-driven-patterns-reference.md](references/event-driven-patterns-reference.md)** — Detailed reference documentation

## Constraints and Warnings

- Events published with `@TransactionalEventListener` only fire after transaction commit
- Avoid publishing large objects in events (memory pressure, serialization issues)
- Be cautious with async event handlers (separate threads, concurrency issues)
- Kafka consumers must handle duplicate messages (implement idempotent processing)
- Event ordering is not guaranteed in distributed systems (design handlers to be order-independent)
- Never perform blocking operations in event listeners on the main transaction thread
- Monitor for event processing backlogs (indicate system capacity issues)

## Related Skills

- `spring-boot-security-jwt` — JWT authentication for secure event publishing
- `spring-boot-test-patterns` — Testing event-driven applications
- `aws-sdk-java-v2-lambda` — Event-driven processing with AWS Lambda
- `langchain4j-tool-function-calling-patterns` — AI-driven event processing

More from giuseppe-trisciuoglio/developer-kit

SkillDescription
adr-draftingCreates new Architecture Decision Record (ADR) documents for significant architectural changes using a consistent template and repository-aware naming and storage guidance. Use when a user or agent decides on an architectural change, needs to document technical rationale, or wants to add a new ADR to the project history.
aws-cdkProvides AWS CDK TypeScript patterns for defining, validating, and deploying AWS infrastructure as code. Use when creating CDK apps, stacks, and reusable constructs, modeling serverless or VPC-based architectures, applying IAM and encryption defaults, or testing and reviewing `cdk synth`, `cdk diff`, and `cdk deploy` changes. Triggers include "aws cdk typescript", "create cdk app", "cdk stack", "cdk construct", "cdk deploy", and "cdk test".
aws-cli-beastProvides advanced AWS CLI patterns for managing EC2, Lambda, S3, DynamoDB, RDS, VPC, IAM, and CloudWatch. Generates bulk operation scripts, automates cross-service workflows, validates security configurations, and executes JMESPath queries for complex filtering. Triggers on "aws cli help", "aws command line", "aws scripting", "aws automation", "aws batch operations", "aws bulk operations", "aws cli pagination", "aws multi-region", "aws profiles", "aws cli troubleshooting".
aws-cloudformation-auto-scalingProvides AWS CloudFormation patterns for Auto Scaling including EC2, ECS, and Lambda. Use when creating Auto Scaling groups, launch configurations, launch templates, scaling policies, lifecycle hooks, and predictive scaling. Covers template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references, and best practices for high availability and cost optimization.
aws-cloudformation-bedrockProvides AWS CloudFormation patterns for Amazon Bedrock resources including agents, knowledge bases, data sources, guardrails, prompts, flows, and inference profiles. Use when creating Bedrock agents with action groups, implementing RAG with knowledge bases, configuring vector stores, setting up content moderation guardrails, managing prompts, orchestrating workflows with flows, and configuring inference profiles for model optimization.
aws-cloudformation-cloudfrontProvides AWS CloudFormation patterns for CloudFront distributions, origins (ALB, S3, Lambda@Edge, VPC Origins), CacheBehaviors, Functions, SecurityHeaders, parameters, Outputs and cross-stack references. Use when creating CloudFront distributions with CloudFormation, configuring multiple origins, implementing caching strategies, managing custom domains with ACM, configuring WAF, and optimizing performance.
aws-cloudformation-cloudwatchProvides AWS CloudFormation patterns for CloudWatch monitoring, metrics, alarms, dashboards, logs, and observability. Use when creating CloudWatch metrics, alarms, dashboards, log groups, log subscriptions, anomaly detection, synthesized canaries, Application Signals, and implementing template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references, and CloudWatch best practices for monitoring production infrastructure.
aws-cloudformation-dynamodbProvides AWS CloudFormation patterns for DynamoDB tables, GSIs, LSIs, auto-scaling, and streams. Use when creating DynamoDB tables with CloudFormation, configuring primary keys, local/global secondary indexes, capacity modes (on-demand/provisioned), point-in-time recovery, encryption, TTL, and implementing template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references.
aws-cloudformation-ec2Provides AWS CloudFormation patterns for EC2 instances, Security Groups, IAM roles, and load balancers. Use when creating EC2 instances, SPOT instances, Security Groups, IAM roles for EC2, Application Load Balancers (ALB), Target Groups, and implementing template structure with Parameters, Outputs, Mappings, Conditions, and cross-stack references.
aws-cloudformation-ecsProvides AWS CloudFormation patterns for ECS clusters, task definitions, services, container definitions, auto scaling, blue/green deployments, CodeDeploy integration, ALB integration, service discovery, monitoring, logging, template structure, parameters, outputs, and cross-stack references. Use when creating ECS clusters with CloudFormation, configuring Fargate and EC2 launch types, implementing blue/green deployments, managing auto scaling, integrating with ALB and NLB, and implementing ECS best practices.