Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Common advice is to not publish domain events outside of your service boundary. They should only exist within your service boundary. Instead, you should publish integration events for other service boundaries. While this general advice makes sense, it’s not so cut-and-dry. There are many reasons why you would want to publish domain events for other services to consume. Here are how I think of Domain Events and Integration Events and when to use them.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
What most people are referring to and generally implement when they talk about Domain Events is within a single boundary. When a state change or something occurs a domain event is published that is then processed by consumers within the same boundary.
This is generally all done in-process. Meaning the consumers all process the same event within the same process as the publisher. There is no message broker or any asynchronous messaging occurring. Everything is done in-memory and in-process. Because of this, this can often be wrapped within the same database transaction. If a consumer throws an exception, it will go up the call stack to the publisher, which can then roll back the transaction.
While this seems on the surface like a good pattern it can oftentimes be less desirable than actually having your consumers in isolation and moving the consumer processing out of process. More on that in a future video/blog post.
Integration events are generally used for integrating with other service boundaries. This then means we’re moving work out of process by leveraging a message broker. Consumers will each individually process the event in isolation without having any effect on the publisher or any other consumers.
Integration events differ from Domain Events in that Domain Events are very specific concepts within a boundary. A domain event may not mean anything or have a perceived different meaning to another boundary. Integration Events are specifically for telling other outside boundaries that something has occurred within a boundary.
Inside vs Outside Events
Generally, Domain Events will be referred to as “Inside Events” because they don’t leave their boundary. While Integration Events are referred to as “Outside Events” because their intent is to leave their boundary.
Why do people recommend not exposing domain events to other boundaries but rather exposing integration events instead? While that recommendation has a good intent, it’s a bit misleading. For me, you can expose domain events outside your boundary if it fits these 3 requirements: Stability, Understanding, and Consumer Requirements.
Events that are stable consider exposing outside your own boundary.
If you’re talking with the business and domain experts are collaboratively determining the various domain events that are a part of a specific boundary, then they are likely to be stable business concepts.
If the business concepts are stable, then your events will be stable and they won’t be likely to change. This is the primary reason why people advocate for Integration Events. Once you expose an event to outside consumers, you have to version them just as you would any API changes that are public.
If you’re using stable business concepts then they aren’t likely to change. Domain Events aren’t for data propagation but rather to indicate what has occurred. This is often very useful for a long-running business process where many different boundaries are involved. Domain events are more behavioral rather than derived from CRUD.
A service boundary can be a linguistic boundary. One concept in one boundary can have a very different meaning in another boundary. Because of this, a domain event in one boundary might not mean anything, or worst have a different perceived meaning in another boundary.
If you’re exposing domain events to other boundaries then there needs to be a clear understanding of what those events are from other boundaries. There must be a level of shared understanding. As mentioned, this often occurs when domain events are used as a way to notify various boundaries within a system that is all a part of a long-running business process.
While the benefit of an event-driven architecture is to decouple producers and consumers, in practice you do actually care about what the consumer requirements are.
There are different purposes for events. Mainly for notification or data propagation. Consumers of events are going to care about events for these two specific reasons.
If they want to consume an event because of data propagation it’s because they want to keep a local cache copy of data that is owned from another boundary. These types of events will often contain a lot more data and often be “fat events” or referred to as Event Carried State Transfer.
If the service wants to consume an event because it’s a part of a long-running business process, then it doesn’t really care about the state so much as it does about simply being notified that an event has occurred, and now it must react and do its part of the business process.
For data propagation, events are often derived from CRUD or Property-based events. For example, ProductUpdated.
While events used for notifications are generally more behavioral. For example, ProductInventoryAdjusted.
Domain Events or Integration Events?
As always, it depends. If your domain events are stable business concepts and they are understood outside of your boundary as a part of a long-running business process, then yes, publishing domain events outside of your boundary are acceptable. If events are used for data propagation or are more CRUD in nature, then publish Integration Events.
Developer-level members of my YouTube channel or Patreon get access to the full source for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.