spring-boot-event-driven-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited spring-boot-event-driven-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
| 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 |
Before (Anti-Pattern):
@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):
@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 for complete working examples.
Create immutable event classes extending a base DomainEvent class:
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 for patterns.
Add domain events to aggregate roots, publish via ApplicationEventPublisher:
@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 for DDD patterns.
Use @TransactionalEventListener for reliable event handling:
@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 for handling patterns.
Configure KafkaTemplate for publishing, @KafkaListener for consuming:
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
value-serializer: org.springframework.kafka.support.serializer.JsonSerializerValidate: Send a test event via KafkaTemplate and confirm it appears in the consumer logs before proceeding to production patterns.
See dependency-setup.md and configuration.md.
Create OutboxEvent entity for atomic event storage:
@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.
Implement retry logic, dead-letter queues, idempotent handlers:
@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.
Enable Spring Cloud Sleuth for distributed tracing, monitor metrics.
ProductCreated (not CreateProduct)@TransactionalEventListener only fire after transaction commitspring-boot-security-jwt — JWT authentication for secure event publishingspring-boot-test-patterns — Testing event-driven applicationsaws-sdk-java-v2-lambda — Event-driven processing with AWS Lambdalangchain4j-tool-function-calling-patterns — AI-driven event processing~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.