How to use Domain Events in Spring?

In the world of Domain-Driven Design (DDD), Domain Events are a powerful way to notify different parts of a system that something meaningful has occurred within the core domain.

Within the Spring ecosystem, event support is simple yet highly effective for decoupling components, improving cohesion, and enabling systems to evolve gracefully. In this post, you’ll understand what Domain Events are, why to use them, and how to implement them in Spring with clarity and structure.

What are Domain Events?

A Domain Event represents a significant occurrence in the domain model, such as:

  • An order was placed
  • A payment was processed
  • A user was registered

Instead of calling other components directly, a service can publish an event, and other parts of the system can listen and react to it — promoting loose coupling and clean architecture.

Benefits of Domain Events

🔁 Loose coupling: the event publisher doesn’t need to know about the listener

♻️ Reusable and extendable: easy to add new behaviors without touching existing code

📦 Separation of concerns: keeps business logic and side effects in separate places

Hands on

Create the Order class

public class Order {

    private Long id;

    public Order(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

Create the event record, can be a class.

public record OrderCreatedEvent(Order order) {

}

Publish the Event

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final ApplicationEventPublisher publisher;

    public OrderService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void createOrder(Order order) {
        publisher.publishEvent(new OrderCreatedEvent(order));
    }
}

This is a service class that receives the ApplicationEventPublisher through dependency injection, so it is possible to publish the event.

Listen for the Event

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class OrderCreatedListener {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("Order generated successfully: " + event.order().getId());
    }
}

Use @EventListener to handle the event.

Conclusion

Domain Events in Spring offer a clean and effective way to apply DDD principles in practice, promoting loose coupling and improving the maintainability of your system. They are perfect for triggering secondary actions without cluttering the core business logic.

Use Domain Events to broadcast meaningful state changes in a scalable and elegant way. And remember: separation is the key to evolution. 🧠✅

Author

  • I am a seasoned Full Stack Software Developer with 8+ years of experience, including 6+ years specializing in Java with Spring and Quarkus. My core expertise lies in developing robust RESTful APIs integrated with Cosmos Db, MySQL, and cloud platforms like Azure and AWS. I have extensive experience designing and implementing microservices architectures, ensuring performance and reliability for high-traffic systems. In addition to backend development, I have experience with Angular to build user-friendly interfaces, leveraging my postgraduate degree in frontend web development to deliver seamless and responsive user experiences. My dedication to clean and secure code led me to present best practices to my company and clients, using tools like Sonar to ensure code quality and security. I am a critical thinker, problem solver, and team player, thriving in collaborative environments while tackling complex challenges. Beyond development, I share knowledge through my blog, NatanCode, where I write about Java, Spring, Quarkus, databases, and frontend development. My passion for learning and delivering innovative solutions drives me to excel in every project I undertake.

    View all posts