I wrote a page about domain events for Eventuous documentation today. It landed as a blog article, so I decided to publish it separately as such.

Concept

If you ever read the Blue Book, you’d notice that the Domain Event concept is not mentioned there. Still, years after the book was published, events have become popular, and domain events in particular.

Eric Evans, the author of the Blue Book, has added the definition to his Domain-Design Reference. Let us start with Eric’s definition:

Model information about activity in the domain as a series of discrete events. Represent each event as a domain object. […] A domain event is a full-fledged part of the domain model, a representation of something that happened in the domain. Ignore irrelevant domain activity while making explicit the events that the domain experts want to track or be notified of, or which are associated with state changes in the other model objects.

When talking about Event Sourcing, we focus on the last bit: “making explicit the events […], which are associated with state changes.” Event Sourcing takes this definition further, and suggests:

Persist the domain objects state as series of domain events. Each domain event represents an explicit state transition. Applying previously recorded events to a domain objects allows us to recover the current state of the object itself.

We can also cite an article from Medium (a bit controversial one):

In the past, the goto statement was widely used in programming languages, before the advent of procedures/functions. The goto statement simply allowed the program to jump to any part of the code during execution. This made it really hard for the developers to answer the question “how did I get to this point of execution?”. And yes, this has caused a large number of bugs. A very similar problem is happening nowadays. Only this time the difficult question is “how did I get to this state” instead of “how did I get to this point of execution”.

Event Sourcing effectively answers this question by giving you a history of all the state transitions for your domain objects, represented as domain events.

So, what this page is about? It doesn’t look like a conventional documentation page, does it? Nevertheless, let’s see how domain events look like when you build a system with Eventuous.

public static class BookingEvents {
public record RoomBooked(
string BookingId,
string RoomId,
LocalDate CheckIn,
LocalDate CheckOut,
decimal Price
);
public record BookingPaid(
string BookingId,
decimal AmountPaid,
bool PaidInFull
);
public record BookingCancelled(string BookingId); public record BookingImported(
string BookingId,
string RoomId,
LocalDate CheckIn,
LocalDate CheckOut
);
}

Oh, that’s it? A record? Yes, a record. Domain events are property bags. Their only purpose is to convey the state transition using the language of your domain. Technically, a domain event should just be an object, which can be serialised and deserialised for the purpose of persistence.

Eventuous dos and donts:

  • Do make sure your domain events can be serialised to a commonly understood format, like JSON.
  • Do make domain events immutable.
  • Do implement equality by value for domain events.
  • Don’t apply things like marker interfaces (or any interfaces) to domain events.
  • Don’t use constructor logic, which can prevent domain events from deserialising.
  • Don’t use value objects in your domain events.

The last point might require some elaboration. The Value Object pattern in DDD doesn’t only require those objects to be immutable and implement equality by value. The main attribute of a value object is that it must be correct. It means that you can try instantiating a value object with invalid arguments, but it will deny them. This characteristic forbids value objects from being used in domain events, as events must be unconditionally deserialisable. No matter what logic your current domain model has, events from the past are equally valid today. By bringing value objects to domain events you make them prone to failure when their validity rules change, which might prevent them from being deserialised. As a result, your aggregates won’t be able to restore their state from previously persistent events and nothing will work.

In addition, I’d like to point out what domain events are and what they are not.

Domain events are facts, which already happened and which force your domain model to transition its state. That’s the entire reason why we can represent the whole domain model state as a series of domain event stored in a database.

Domain events are neither notifications nor integration events. You can produce both of those from domain events though. What’s the difference you might ask? Domain events are a part of your domain model, which use your Ubiquitous Language and contain your internal information. For you to be able to iterate on the model, you must be able to change your domain events schema at any time. However, that doesn’t apply to anything that gets out of your context boundary. As soon as you publish any event to the outside world, it becomes a contract, which you must fulfil. You never know who consumes your events, and you definitely don’t want to get suck by not being able to develop your own model because someone else depends on its internals. Always consider your external (public) events as the contract for your system. Keep your domain events inside your model, make them private. Convert private events to public events when necessary.

Alexey is the Event Sourcing and Domain-Driven Design enthusiast and promoter. He works as a Developer Advocate at Event Store and Chief Architect at ABAX.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store