public abstract class Event implements Serializable {
private static final long serialVersionUID = 1L
public final UUID id = UUID.randomUUID();
public final Date created;
public Event() {
this.created = new Date();
}
}Domain Events, Event Sourcing, and CQRS
Gerson Sunyé gerson.sunye@univ-nantes.fr
Represents something significant that has happened within a specific Bounded Context.
Named in the past tense to indicate that they have already occurred.
OrderPlaced
ProductShipped
CustomerRegistered
Events capture meaningful changes in the domain and provide a clear historical record of what has happen.

Image from Microsoft Learn
Reduces coupling
Events, allow decoupling different parts of your system.
Audit Trails and Debugging
Domain Events serve as an audit trail of everything that has happened in your system.
Scalability
In a distributed system or microservices architecture, Domain Events are instrumental.
public abstract class Event implements Serializable {
private static final long serialVersionUID = 1L
public final UUID id = UUID.randomUUID();
public final Date created;
public Event() {
this.created = new Date();
}
}New way of persisting application state as an ordered sequence of events.
The application can selectively query these events and reconstruct the state of the application at any point in time.
To make this work, we need to represent every change to the state of the application as events

Image from Baeldung
Events here are facts that have happened and can not be altered: they must be immutable.
Recreating the application state is just a matter of replaying all the events.
This also opens up the possibility to replay events selectively, replay some events in reverse, and much more. |
About segregating the command and query side of the application architecture.
Based on the Command Query Separation (CQS) principle which was suggested by Bertrand Meyer.
CQS suggests that we divide the operations on domain objects into two distinct categories: Queries and Commands
Queries return a result and do not change the observable state of a system.
Commands change the state of the system but do not necessarily return a value.

Image from Baeldung
We achieve this by cleanly separating the Command and Query sides of the domain model.
We can take a step further, splitting the write and read side of the data store as well, of course, by introducing a mechanism to keep them in sync.
Same domain model for read and write operations
Only the last state is persisted
Provides a convenient way to select separate domain models appropriate for write and read operations;
We don’t have to create a complex domain model supporting both
Helps to select repositories that are individually suited for handling the complexities of the read and write operations, like high throughput for writing and low latency for reading
Naturally complements event-based programming models in a distributed architecture by providing a separation of concerns as well as simpler domain models
Only a complex domain model can benefit from the added complexity of this pattern; a simple domain model can be managed without all this
Naturally leads to code duplication to some extent, which is an acceptable evil compared to the gain it leads us to; however, individual judgment is advised
Separate repositories lead to problems of consistency, and it’s difficult to keep the write and read repositories in perfect sync always; we often have to settle for eventual consistency
| There are several in-memory databases to handle domain events, e.g. Apache Druid, Apache Kafka, or Apache Cassandra. |
Makes write operations much faster as there is no read, update, and write required; write is merely appending an event to a log
Removes the object-relational impedance and, hence, the need for complex mapping tools; of course, we still need to recreate the objects back
Happens to provide an audit log as a by-product, which is completely reliable; we can debug exactly how the state of a domain model has changed
It makes it possible to support temporal queries and achieve time-travel (the domain state at a point in the past)!
It’s a natural fit for designing loosely coupled components in a microservices architecture that communicate asynchronously by exchanging messages
There’s a learning curve associated and a shift in mindset required to adopt event sourcing; it’s not intuitive, to begin with
It makes it rather difficult to handle typical queries as we need to recreate the state unless we keep the state in the local cache
Although it can be applied to any domain model, it’s more appropriate for the event-based model in an event-driven architecture

Image from Baeldung
We already implemented:
CQRS and
Event Sourcing
Now, we can put things together:
The Aggregate generates Events
The Projector processes events instead of Domain Object instances
| Now, the Aggregate generates events |
Use different repositories for writing and querying models
Persist models as a sequence of events
Not adapted for simple domain models, like the User/Address/Contact example
Benefits only appear for reasonably complex domain models