Careeroza
Producing Events with Spring Kafka
The Event and the Topic
Add spring-kafka (Spring for Apache Kafka; Spring Boot manages the version). Define the event as a record. Put an eventId in every event so consumers can detect duplicates:
public record OrderPlacedEvent(
String eventId, // UUID, for idempotent consumers
Long orderId,
Long userId,
BigDecimal totalAmount,
List<Item> items,
Instant occurredAt) {
public record Item(Long productId, int quantity) {}
}
@Configuration
class KafkaTopicConfig {
@Bean
NewTopic orderPlacedTopic() { // created at startup if missing
return TopicBuilder.name("order-placed")
.partitions(3)
.replicas(1) // 3 in production
.build();
}
}Sending with KafkaTemplate
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderEventPublisher {
private final KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate;
public void orderPlaced(Order order) {
OrderPlacedEvent event = new OrderPlacedEvent(
UUID.randomUUID().toString(), order.getId(), order.getUserId(), order.getTotal(),
order.getItems().stream().map(i -> new OrderPlacedEvent.Item(i.getProductId(), i.getQuantity())).toList(),
Instant.now());
// key = orderId: all events of one order stay in order on one partition
kafkaTemplate.send("order-placed", String.valueOf(order.getId()), event)
.whenComplete((result, ex) -> {
if (ex != null) {
log.error("Could not publish OrderPlaced {}", order.getId(), ex);
} else {
log.info("OrderPlaced {} -> partition {} offset {}", order.getId(),
result.getRecordMetadata().partition(), result.getRecordMetadata().offset());
}
});
}
}order-service application.yml
application.yml
spring:
kafka:
bootstrap-servers: ${KAFKA_SERVERS:localhost:9092}
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
acks: all # wait until all in-sync replicas have it
properties:
enable.idempotence: true # no duplicates from producer retries
spring.json.add.type.headers: falseExample output
INFO c.s.order.OrderEventPublisher : OrderPlaced 1001 -> partition 2 offset 17
$ docker exec kafka /opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--topic order-placed --from-beginning --property print.key=true
1001 {"eventId":"6b1f0c2e-8d3a-4f7b-9e21-5c4d3a2b1f0e","orderId":1001,"userId":42,"totalAmount":2998.00,
"items":[{"productId":7,"quantity":2}],"occurredAt":"2026-10-14T10:15:30.123Z"}Note: send() is asynchronous: it returns a CompletableFuture<SendResult>. If you save the order in the database and then publish, a crash in between loses the event. The outbox pattern (advance level) fixes that.