Domain events: simple and reliable solution

By Vladimir Khorikov

Today, I’d like to write about a simple and reliable way to implement domain events.

Domain events: dispatching before committing

I believe it was Udi Dahan who first introduced the concept of domain events specific to Domain-Driven Design. The idea is simple: if you want to indicate an event that is significant to your domain, raise this event explicitly and let the other classes in your domain model subscribe and react to it.

So basically do the following. Create a domain event:

Then introduce a static class that will allow for subscribing to the events and raising them:

Now as you have this infrastructure in place, you can start using it. This is what a domain event producer can look like:

And this an example of a consumer. It gathers statistics for all submitted orders and saves it to the database:

I will call this classic approach to domain events dispatching before committing. That’s because it allows you to raise and react to an event before the business transaction is committed.

There are several problems with this approach. It can be useful at times but falls short when the side effects of processing the domain events span across multiple transactions.

Let me explain what I mean by that. Let’s say for example that you want to add another consumer to the above domain event. This consumer would send notification emails to users who submit their orders:

Now, the act of sending an email brings one more transaction to the table. Before that, both the producer of the domain event and the consumer of it generated side effects that were stored in the same database. The side effect of the producer was the Order class instance, and the side effect of the consumer was changed stats info. You could create an overarching database transaction so that if the system fails to persist the order for some reason, the changes to the order statistics would not be persisted either.

After adding the OrderNotification class, this is no longer the case. Sending emails works within its own transaction boundaries and you can’t tie it to the database one. By sending notification emails before ensuring the database transaction is completed, you are opening the application for potential inconsistency issues. Now it is possible to have an email sent without persisting the order. Which could happen if, for example, the database goes down at the time of saving that order.

Some people argue that, sure, this approach would not work in such scenarios, but you are still able to use it when there are no external systems involved. In other words, if all consumers operate within the same database transaction scope, like OrderStats.

This is a poor argument. If all the consumers of an event reside within the same database transaction, domain events add very little value. If all the collaborating parties operate upon the same database, it’s better to make the flow of the domain logic explicit and avoid dealing with domain events. Domain events bring significant complexity overhead in the form of indirection. If you are able to avoid this overhead, I advocate you do that.

So, if OrderStats is the only consumer of the OrderSubmitted event, you can re-write the example above into something like this:

and get rid of the domain events infrastructure altogether. This program flow gives a much better understanding of what is going on during the request. All steps are outlined explicitly here.

This is the reason why I don’t recommend using the “dispatch before committing” approach. It cannot be used if consumers produce side effects that lie outside of the database transaction scope. And for all other types of side effects, you would be better off not using domain events anyway.

When it comes to the topic of domain events, the general rule is this: employ them for inter-application communication only, when you need to incur side effects that span beyond just your database. For inner-application communication, get your program flow straight and explicit. Use regular techniques, like returning a result of an operation and passing it to a next method.

Jimmy Bogard wrote about another domain events pattern a few years back. The difference between Udi’s and Jimmy’s approaches is that the latter suggests separating the two steps that comprise raising a domain event: creation and dispatching. It makes the events more testable but essentially, it is the same “dispatch before committing” approach: domain events get dispatched before the database transaction is committed.

Domain events: committing before dispatching

A couple years ago I too wrote about domain events in my Domain-Driven Design in Practice training course. I called my implementation “a better approach” for the lack of a better name. But now I would like to rename it to committing before dispatching.

As you guessed from the name already, this approach involves committing the database transaction and only after that dispatching domain events. The basic idea is similar to what Jimmy Bogard described in his blog post: you shouldn’t dispatch the events right away and instead need to keep track of them until you are ready to commit the database transaction. The main difference here is that you dispatch those events after the transaction is committed, not before.

Here’s what the producer of the event would look like:

Note that Order no longer works with the static DomainEvents class to dispatch those events. Instead, it saves them to the internal collection. By the way, this opens an opportunity to also merge events into a single one or cancel previous events. Which wasn’t possible with the previous approach because the events were dispatched right away.

To dispatch the events, I used NHibernate’s event listeners. Particularly, those events that trigger after the database transaction is committed (taken from here):

This ensures that the dispatch will be executed only after the data is persisted. Note the OnPostUpdateCollection listener in the code above. It allows you to dispatch events even if the entity itself has not been changed. For example, when you add a line to an order but keep the order itself unchanged. Took me long time to figure out how to deal with those use cases 🙂

The “commit before dispatching” approach is a big improvement over the “dispatch before committing” one. Now you are able to incur any side effects in your domain event consumers, be they sending an email, putting a message on a bus, calling a third-party API, etc. You will no longer run into a situation when your database transaction fails but the confirmation email is already sent to the customer.

Domain events: simple and reliable solution

However, even with this improvement, there still are two drawbacks to that solution:

  • You need an ORM to implement it. And not just any ORM but the one which supports post-update, -delete, etc events. NHibernate is great at that but maybe you can’t use it for some reason.
  • You can still run into inconsistencies. They are not as severe as those you encounter with dispatching before committing but they still can happen. If your email grid fails to accept the notification email, you will end up with a submitted order but with no confirmation email. You can’t make the dispatch of domain events 100% consistent with committing the database transaction.

I’m saying that such inconsistencies are not as severe because it’s pretty easy to mitigate them. A reliable queue on top of external, potentially faulty calls helps a lot. It significantly reduces the chances of running into the inconsistency (but of course doesn’t eliminate it completely because the reliable queue can also potentially go down).

So what is that simple and reliable solution that doesn’t have all those drawbacks?

It’s persisting domain events along with domain objects. What you need to do is add a table to the database:

Domain events: simple and reliable solution

Domain events: simple and reliable solution

and persist events into it like regular domain objects. Then, have a separate worker that picks those events one by one and processes them. In most cases that processing would involve pushing a message on a bus which then can fan it to the appropriate subscribers. But you can also come up with your own pub-sub mechanism.

The benefit of this approach is that you now have a 100% certainty in your domain events infrastructure. Persisting domain events allows you to achieve full consistency between the producers and consumers of those events. Producers are able to generate events only if they themselves are persisted (Order class in the example above). And consumers have a chance to implement a retry mechanism so that no domain events slip through the cracks. It also helps that the consumer resides in a separate process.

There’s one drawback to this approach, though. It requires more effort to implement compared to the previous one. It’s simple and reliable, but not easy. You will need to introduce the additional table and you will also need to develop a background job for that purpose.

I usually go with the “committing before dispatching” approach. All my event consumers usually do is put a message on a bus anyway, which is a quite reliable operation. I haven’t had any issues with it in production. But the manual approach is good too, especially taking into account all the benefits I described above.

Summary

  • Use domain events for inter-application communication only. For inner-application communication, use explicit program flow instead.
  • Dispatch before committing is when you dispatch an event before the database transaction is completed. Avoid this type of domain events as you won’t be able to use it for inter-application communication.
  • Commit before dispatching is when you dispatch an event after the database transaction is completed. This approach is good in most cases. There’s still a small chance of inconsistency with this implementation.
  • Persist domain events along with domain objects if you need 100% consistency and can’t use an ORM. This approach requires more work.

Be sure to check out my Domain-Driven Design in Practice training course if you want to learn more about DDD in general and domain events in particular.





  • http://cminor.io cminor

    Hello Vladimir,

    I would like to thank you for the interesting post. Some comments from my side:
    The approach you are proposing is not really new, in fact this is exactly how you code and persist event sourced entities. Although in your case you still have a Memento pattern going on (the OrderLine) you can easily fit this item into an appropriate event and in the end you can persist (and rebuilt) your model only from events. This will greatly simplify your data layer (no need for ORM at all, Repositories will only need to have 2 methods: fetchById and save) but sometimes it creates a bit of complexity that not all teams want to deal with. Lastly as far as publishing an event is concerned, the approach is not different than Committing and Publishing. It is the exact same thing but instead of synchronously publishing the events after commit, you are doing it async. As with all other great tools async comes with a cost, so we must really do our analysis and decide how much we want to invest in this compared to how much we will gain by adding another component-worker to the game, or just play synchronously even though there always is the chance that the highly available, robust & scalable Messaging Fabric might fail 🙂

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thanks for the comment, great points.

      I’m not claiming the authorship of this idea, most ideas in programming has already been invented anyway 🙂 And indeed, it’s a step towards an event sourced architecture.

      Regarding that the last approach is not different than Committing and Publishing other than being async. Well, that’s the whole point, it’s the only way to ensure full consistency.

  • Danil Suits

    It sounds as though you are advocating the pattern that Udi described in 2014?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Similar approach, indeed.

  • Krzysztof Sierżęga

    Hello Vladmir,
    Check this solution for dispatching events after transaction successful. We are using it and it also works great 🙂 http://www.janblaha.net/blog/run-action-after-transaction-commit-using-nhibernate

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Looks interesting, didn’t know about that feature. Will try to play with it, thanks

  • Christopher McEwen

    Nice post Vladmir 🙂

    I’m quite new to DDD and was wondering in your scenario, for dispatching before committing, wouldn’t you just expect both events to complete eventually?

    So our handler for saving the data to the database would just keep retrying (Message Queue) and so would our email handler. Eventually both would be complete.

    Or implement a two step check where the handler for sending the email checks the record is in the database before it sends the email. If the record isn’t in the database then it sits on the queue and retries.

    Is this not a viable workaround for dispatching before committing?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      The problem is that if you dispatch events before the action they are generated by is committed, the event processors can’t check anything as there’s nothing in the database yet.

  • AreQ_BAH

    Commit before dispatching implementation:
    What about using your app service for raising an event? There you will be sure, that it will ocure after entity was saved to the DB. And you dont need to leverage AOP – whitch is introducing a bit of ‘auto-magic’ into the workflow.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      You can do that but that would be a lot of repetitive code as you’ll need to put it to every method in your app services. It is a viable option for simpler code bases, though.

  • Hristo Yankov

    Hi,

    Can you elaborate on the use of domain events in “inter-application” development? I just don’t see it. If two applications know about the same domain event, in my view they are just separate components of the same application. OK, that’s arguable. But how would you send the domain event to another application? It would require some kind of infrastructure – a Queue, database record, http connection, whatever … I mean, that would go way outside the simple Subscribe and Post notification, shown in this post.

    More importantly, I was under the impression that DDD pushes persistence to the very edge of the implementation and makes it a minor, if not even an optional concern. Your last approach suggests that we NEED persistence, in order to have Domain Events, no? Don’t get me wrong, I like it, I am just trying to understand the implications.

    Great post, nonetheless!

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Regarding the inter-application communication. It basically means that the event handlers incur side effects that don’t lie within your main data storage. It can be a call to the SMTP service to send an email or a call to the bus to submit a message to the publisher.

      Inter-application communication doesn’t necessarily mean that you have two business applications/microservices that communicate with each other. You can do that if your handlers call other microservices/apps directly via their APIs but that wouldn’t be a good solution. Usually what happens is your handlers reach out to a queue/bus. So communication here is between your app and the bus, not between your app and the downstream apps. What happens with the messages you put on the bus afterwards is not your app’s concern.

      If two applications know about the same domain event, in my view they are just separate components of the same application.

      If by domain event you mean the actual domain event class then yes, I agree with you. Two different code bases shouldn’t couple to the same domain even implementation unless they belong to the same bounded context. However, it’s OK for two apps to couple to a data contract, which is the format of the messages that travel between applications. You need to know how to interpret them in order to communicate with other apps.

      Regarding persistence, I don’t see a contradiction here. You do make the domain model agnostic to the fact that all its entities/value objects/etc are stored somewhere. Similarly, your domain event handlers don’t have to know where the events they are processing come from. Just like a User entity doesn’t need to know that it is stored in a relational database. The persistence concern is still there, it’s just separate from the domain logic.

  • Harry McIntyre

    The bit about not using them for inner-application development is very important – the code-base I’m working on uses them for internally raising behaviour, but all it has done is obscure what happens.

    In your above code, what happens if the post-commit code fails? I would have thought that it would be better to use a pre-commit hook, AND only write services which support transactions (so that the change can be rolled back along with the rest of the unit of work). Otherwise you might not end up sending that password-reset email, right?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      uses them for internally raising behaviour, but all it has done is obscure what happens

      I would compare them with regular .NET even handlers. Those events are good in some scenarios (like handling events from the UI: button click, etc) but definitely not in the domain model.

      That’s correct, if the post-commit fails, you will end up not sending the email. I don’t think a distributed transaction is a good option, though. Too much overhead in order to setup and run it. I would prefer putting a (more) reliable external system in front of the SMTP server instead, like an Azure queue or an AWS SNS/SQS.

  • Julio Gonzalez

    Hi Vladimir,
    Thanks for the interesting article. Wouldn’t you prefer a messaging infrastructure with guaranteed delivery (Rmq or similar) rather than your approach of persisting domain events along with domain objects? I don’t see the need of storing the domain event on DB and having something constantly polling it?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      I would. I personally prefer not introducing this additional complexity of keeping the domain events in the database and usually go with Commit before Dispatch. The chance of failing to send a domain event is low enough, so trading that chance for simpler code is usually worth it.